/* -*- 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/poly.hxx>
 
#include <vcl/builder.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/image.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/decoview.hxx>
#include <vcl/event.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/toolkit/dialog.hxx>
#include <vcl/toolkit/fixed.hxx>
#include <vcl/toolkit/button.hxx>
#include <vcl/salnativewidgets.hxx>
#include <vcl/toolkit/edit.hxx>
#include <vcl/layout.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/uitest/uiobject.hxx>
 
#include <bitmaps.hlst>
#include <svdata.hxx>
#include <window.h>
#include <vclstatuslistener.hxx>
#include <osl/diagnose.h>
 
#include <comphelper/base64.hxx>
#include <comphelper/dispatchcommand.hxx>
#include <comphelper/lok.hxx>
#include <officecfg/Office/Common.hxx>
#include <boost/property_tree/ptree.hpp>
#include <tools/json_writer.hxx>
#include <tools/stream.hxx>
 
 
using namespace css;
 
constexpr auto PUSHBUTTON_VIEW_STYLE = WB_3DLOOK |
                                     WB_LEFT | WB_CENTER | WB_RIGHT |
                                     WB_TOP | WB_VCENTER | WB_BOTTOM |
                                     WB_WORDBREAK | WB_NOLABEL |
                                     WB_DEFBUTTON | WB_NOLIGHTBORDER |
                                     WB_RECTSTYLE | WB_SMALLSTYLE |
                                     WB_TOGGLE;
constexpr auto RADIOBUTTON_VIEW_STYLE = WB_3DLOOK |
                                     WB_LEFT | WB_CENTER | WB_RIGHT |
                                     WB_TOP | WB_VCENTER | WB_BOTTOM |
                                     WB_WORDBREAK | WB_NOLABEL;
constexpr auto CHECKBOX_VIEW_STYLE = WB_3DLOOK |
                                     WB_LEFT | WB_CENTER | WB_RIGHT |
                                     WB_TOP | WB_VCENTER | WB_BOTTOM |
                                     WB_WORDBREAK | WB_NOLABEL;
 
#define STYLE_RADIOBUTTON_MONO      (sal_uInt16(0x0001)) // legacy
#define STYLE_CHECKBOX_MONO         (sal_uInt16(0x0001)) // legacy
 
class ImplCommonButtonData
{
public:
    ImplCommonButtonData();
 
    tools::Rectangle       maFocusRect;
    tools::Long            mnSeparatorX;
    DrawButtonFlags mnButtonState;
    bool            mbSmallSymbol;
    bool            mbGeneratedTooltip;
 
    Image           maImage;
    ImageAlign      meImageAlign;
    SymbolAlign     meSymbolAlign;
 
    Image           maCustomContentImage;
 
    /** StatusListener. Updates the button as the slot state changes */
    rtl::Reference<VclStatusListener<Button>> mpStatusListener;
};
 
ImplCommonButtonData::ImplCommonButtonData() :  mnSeparatorX(0), mnButtonState(DrawButtonFlags::NONE),
mbSmallSymbol(false), mbGeneratedTooltip(false),  meImageAlign(ImageAlign::Top), meSymbolAlign(SymbolAlign::LEFT)
{
}
 
Button::Button( WindowType nType ) :
    Control( nType ),
    mpButtonData( std::make_unique<ImplCommonButtonData>() )
{
}
 
Button::~Button()
{
    disposeOnce();
}
 
void Button::dispose()
{
    if (mpButtonData->mpStatusListener.is())
        mpButtonData->mpStatusListener->dispose();
    Control::dispose();
}
 
void Button::SetCommandHandler(const OUString& aCommand, const css::uno::Reference<css::frame::XFrame>& rFrame)
{
    maCommand = aCommand;
    SetClickHdl( LINK( this, Button, dispatchCommandHandler) );
 
    mpButtonData->mpStatusListener = new VclStatusListener<Button>(this, rFrame, aCommand);
    mpButtonData->mpStatusListener->startListening();
}
 
void Button::Click()
{
    ImplCallEventListenersAndHandler( VclEventId::ButtonClick, [this] () { maClickHdl.Call(this); } );
}
 
void Button::SetModeImage( const Image& rImage )
{
    if ( rImage != mpButtonData->maImage )
    {
        mpButtonData->maImage = rImage;
        StateChanged( StateChangedType::Data );
        queue_resize();
    }
}
 
Image const & Button::GetModeImage( ) const
{
    return mpButtonData->maImage;
}
 
bool Button::HasImage() const
{
    return !!(mpButtonData->maImage);
}
 
void Button::SetImageAlign( ImageAlign eAlign )
{
    if ( mpButtonData->meImageAlign != eAlign )
    {
        mpButtonData->meImageAlign = eAlign;
        StateChanged( StateChangedType::Data );
    }
}
 
ImageAlign Button::GetImageAlign() const
{
    return mpButtonData->meImageAlign;
}
 
void Button::SetCustomButtonImage(const Image& rImage)
{
    if (rImage != mpButtonData->maCustomContentImage)
    {
        mpButtonData->maCustomContentImage = rImage;
        StateChanged( StateChangedType::Data );
    }
}
 
Image const & Button::GetCustomButtonImage() const
{
    return mpButtonData->maCustomContentImage;
}
 
tools::Long Button::ImplGetSeparatorX() const
{
    return mpButtonData->mnSeparatorX;
}
 
void Button::ImplSetSeparatorX( tools::Long nX )
{
    mpButtonData->mnSeparatorX = nX;
}
 
DrawTextFlags Button::ImplGetTextStyle( WinBits nWinStyle, SystemTextColorFlags nSystemTextColorFlags ) const
{
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
    DrawTextFlags nTextStyle = FixedText::ImplGetTextStyle(nWinStyle & ~WB_DEFBUTTON);
 
    if (!IsEnabled())
        nTextStyle |= DrawTextFlags::Disable;
 
    if ((nSystemTextColorFlags & SystemTextColorFlags::Mono) ||
        (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
    {
        nTextStyle |= DrawTextFlags::Mono;
    }
 
    return nTextStyle;
}
 
void Button::ImplDrawAlignedImage(OutputDevice* pDev, Point& rPos,
                                  Size& rSize,
                                  sal_Int32 nImageSep,
                                  DrawTextFlags nTextStyle, tools::Rectangle *pSymbolRect,
                                  bool bAddImageSep)
{
    OUString aText(GetText());
    bool bDrawImage = HasImage();
    bool bDrawText  = !aText.isEmpty();
    bool bHasSymbol = pSymbolRect != nullptr;
 
    // No text and no image => nothing to do => return
    if (!bDrawImage && !bDrawText && !bHasSymbol)
        return;
 
    WinBits nWinStyle = GetStyle();
    tools::Rectangle aOutRect( rPos, rSize );
    ImageAlign eImageAlign = mpButtonData->meImageAlign;
    Size aImageSize = mpButtonData->maImage.GetSizePixel();
 
    aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
    aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
 
    // Drawing text or symbol only is simple, use style and output rectangle
    if (bHasSymbol && !bDrawImage && !bDrawText)
    {
        *pSymbolRect = aOutRect;
        return;
    }
    else if (bDrawText && !bDrawImage && !bHasSymbol)
    {
        aOutRect = DrawControlText(*pDev, aOutRect, aText, nTextStyle, nullptr, nullptr);
        tools::Rectangle textRect = GetTextRect(
            tools::Rectangle(Point(), Size(0x7fffffff, 0x7fffffff)), aText, nTextStyle);
        // If the button text doesn't fit into it, put it into a tooltip (might happen in sidebar)
        if (GetQuickHelpText()!= aText && mpButtonData->mbGeneratedTooltip)
            SetQuickHelpText(u""_ustr);
        if (GetQuickHelpText().isEmpty() && textRect.getOpenWidth() > rSize.getWidth())
        {
            SetQuickHelpText(aText);
            mpButtonData->mbGeneratedTooltip = true;
        }
 
        ImplSetFocusRect(aOutRect);
        rSize = aOutRect.GetSize();
        rPos = aOutRect.TopLeft();
 
        return;
    }
 
    // check for HC mode ( image only! )
    Image* pImage = &(mpButtonData->maImage);
 
    Size aTextSize;
    Size aSymbolSize;
    Size aDeviceTextSize;
    Point aImagePos = rPos;
    Point aTextPos = rPos;
    tools::Rectangle aUnion(aImagePos, aImageSize);
    tools::Long nSymbolHeight = 0;
 
    if (bDrawText || bHasSymbol)
    {
        // Get the size of the text output area ( the symbol will be drawn in
        // this area as well, so the symbol rectangle will be calculated here, too )
 
        tools::Rectangle aRect(Point(), rSize);
        Size aTSSize;
 
        if (bHasSymbol)
        {
            tools::Rectangle aSymbol;
            if (bDrawText)
            {
                nSymbolHeight = pDev->GetTextHeight();
                if (mpButtonData->mbSmallSymbol)
                    nSymbolHeight = nSymbolHeight * 3 / 4;
 
                aSymbol = tools::Rectangle(Point(), Size(nSymbolHeight, nSymbolHeight));
                ImplCalcSymbolRect(aSymbol);
                aRect.AdjustLeft(3 * nSymbolHeight / 2 );
                aTSSize.setWidth( 3 * nSymbolHeight / 2 );
            }
            else
            {
                aSymbol = tools::Rectangle(Point(), rSize);
                ImplCalcSymbolRect(aSymbol);
                aTSSize.setWidth( aSymbol.GetWidth() );
            }
            aTSSize.setHeight( aSymbol.GetHeight() );
            aSymbolSize = aSymbol.GetSize();
        }
 
        if (bDrawText)
        {
            if ((eImageAlign == ImageAlign::LeftTop)     ||
                (eImageAlign == ImageAlign::Left )        ||
                (eImageAlign == ImageAlign::LeftBottom)  ||
                (eImageAlign == ImageAlign::RightTop)    ||
                (eImageAlign == ImageAlign::Right)        ||
                (eImageAlign == ImageAlign::RightBottom))
            {
                aRect.AdjustRight( -sal_Int32(aImageSize.Width() + nImageSep) );
            }
            else if ((eImageAlign == ImageAlign::TopLeft)    ||
                     (eImageAlign == ImageAlign::Top)         ||
                     (eImageAlign == ImageAlign::TopRight)   ||
                     (eImageAlign == ImageAlign::BottomLeft) ||
                     (eImageAlign == ImageAlign::Bottom)      ||
                     (eImageAlign == ImageAlign::BottomRight))
            {
                aRect.AdjustBottom( -sal_Int32(aImageSize.Height() + nImageSep) );
            }
 
            aRect = GetControlTextRect(*pDev, aRect, aText, nTextStyle, &aDeviceTextSize);
            aTextSize = aRect.GetSize();
 
            aTSSize.AdjustWidth(aTextSize.Width() );
 
            if (aTSSize.Height() < aTextSize.Height())
                aTSSize.setHeight( aTextSize.Height() );
 
            if (bAddImageSep && bDrawImage)
            {
                tools::Long nDiff = (aImageSize.Height() - aTextSize.Height()) / 3;
                if (nDiff > 0)
                    nImageSep += nDiff;
            }
        }
 
        Size aMax;
        aMax.setWidth( std::max(aTSSize.Width(), aImageSize.Width()) );
        aMax.setHeight( std::max(aTSSize.Height(), aImageSize.Height()) );
 
        // Now calculate the output area for the image and the text according to the image align flags
 
        if ((eImageAlign == ImageAlign::Left) ||
            (eImageAlign == ImageAlign::Right))
        {
            aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
            aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
        }
        else if ((eImageAlign == ImageAlign::LeftBottom) ||
                 (eImageAlign == ImageAlign::RightBottom))
        {
            aImagePos.setY( rPos.Y() + aMax.Height() - aImageSize.Height() );
            aTextPos.setY( rPos.Y() + aMax.Height() - aTSSize.Height() );
        }
        else if ((eImageAlign == ImageAlign::Top) ||
                 (eImageAlign == ImageAlign::Bottom))
        {
            aImagePos.setX( rPos.X() + (aMax.Width() - aImageSize.Width()) / 2 );
            aTextPos.setX( rPos.X() + (aMax.Width() - aTSSize.Width()) / 2 );
        }
        else if ((eImageAlign == ImageAlign::TopRight) ||
                 (eImageAlign == ImageAlign::BottomRight))
        {
            aImagePos.setX( rPos.X() + aMax.Width() - aImageSize.Width() );
            aTextPos.setX( rPos.X() + aMax.Width() - aTSSize.Width() );
        }
 
        if ((eImageAlign == ImageAlign::LeftTop) ||
            (eImageAlign == ImageAlign::Left)     ||
            (eImageAlign == ImageAlign::LeftBottom))
        {
            aTextPos.setX( rPos.X() + aImageSize.Width() + nImageSep );
        }
        else if ((eImageAlign == ImageAlign::RightTop) ||
                 (eImageAlign == ImageAlign::Right)     ||
                 (eImageAlign == ImageAlign::RightBottom))
        {
            aImagePos.setX( rPos.X() + aTSSize.Width() + nImageSep );
        }
        else if ((eImageAlign == ImageAlign::TopLeft) ||
                 (eImageAlign == ImageAlign::Top)      ||
                 (eImageAlign == ImageAlign::TopRight))
        {
            aTextPos.setY( rPos.Y() + aImageSize.Height() + nImageSep );
        }
        else if ((eImageAlign == ImageAlign::BottomLeft) ||
                 (eImageAlign == ImageAlign::Bottom)      ||
                 (eImageAlign == ImageAlign::BottomRight))
        {
            aImagePos.setY( rPos.Y() + aTSSize.Height() + nImageSep );
        }
        else if (eImageAlign == ImageAlign::Center)
        {
            aImagePos.setX( rPos.X() + (aMax.Width()  - aImageSize.Width()) / 2 );
            aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
            aTextPos.setX( rPos.X() + (aMax.Width()  - aTSSize.Width()) / 2 );
            aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
        }
        aUnion = tools::Rectangle(aImagePos, aImageSize);
        aUnion.Union(tools::Rectangle(aTextPos, aTSSize));
    }
 
    // Now place the combination of text and image in the output area of the button
    // according to the window style (WinBits)
    tools::Long nXOffset = 0;
    tools::Long nYOffset = 0;
 
    if (nWinStyle & WB_CENTER)
    {
        nXOffset = (rSize.Width() - aUnion.GetWidth()) / 2;
    }
    else if (nWinStyle & WB_RIGHT)
    {
        nXOffset = rSize.Width() - aUnion.GetWidth();
    }
 
    if (nWinStyle & WB_VCENTER)
    {
        nYOffset = (rSize.Height() - aUnion.GetHeight()) / 2;
    }
    else if (nWinStyle & WB_BOTTOM)
    {
        nYOffset = rSize.Height() - aUnion.GetHeight();
    }
 
    // the top left corner should always be visible, so we don't allow negative offsets
    if (nXOffset < 0) nXOffset = 0;
    if (nYOffset < 0) nYOffset = 0;
 
    aImagePos.AdjustX(nXOffset );
    aImagePos.AdjustY(nYOffset );
    aTextPos.AdjustX(nXOffset );
    aTextPos.AdjustY(nYOffset );
 
    // set rPos and rSize to the union
    rSize = aUnion.GetSize();
    rPos.AdjustX(nXOffset );
    rPos.AdjustY(nYOffset );
 
    if (bHasSymbol)
    {
        if (mpButtonData->meSymbolAlign == SymbolAlign::RIGHT)
        {
            Point aRightPos(aTextPos.X() + aTextSize.Width() + aSymbolSize.Width() / 2, aTextPos.Y());
            *pSymbolRect = tools::Rectangle(aRightPos, aSymbolSize);
        }
        else
        {
            *pSymbolRect = tools::Rectangle(aTextPos, aSymbolSize);
            aTextPos.AdjustX(3 * nSymbolHeight / 2 );
        }
        if (mpButtonData->mbSmallSymbol)
        {
            nYOffset = (aUnion.GetHeight() - aSymbolSize.Height()) / 2;
            pSymbolRect->SetPosY(aTextPos.Y() + nYOffset);
        }
    }
 
    DrawImageFlags nStyle = DrawImageFlags::NONE;
 
    if (!IsEnabled())
    {
        nStyle |= DrawImageFlags::Disable;
    }
 
    if (IsZoom())
        pDev->DrawImage(aImagePos, aImageSize, *pImage, nStyle);
    else
        pDev->DrawImage(aImagePos, *pImage, nStyle);
 
    if (bDrawText)
    {
        const tools::Rectangle aTOutRect(aTextPos, aTextSize);
        ImplSetFocusRect(aTOutRect);
        DrawControlText(*pDev, aTOutRect, aText, nTextStyle, nullptr, nullptr, &aDeviceTextSize);
    }
    else
    {
        ImplSetFocusRect(tools::Rectangle(aImagePos, aImageSize));
    }
}
 
void Button::ImplSetFocusRect(const tools::Rectangle &rFocusRect)
{
    tools::Rectangle aFocusRect = rFocusRect;
    tools::Rectangle aOutputRect(Point(), GetOutputSizePixel());
 
    if (!aFocusRect.IsEmpty())
    {
        aFocusRect.AdjustLeft( -1 );
        aFocusRect.AdjustTop( -1 );
        aFocusRect.AdjustRight( 1 );
        aFocusRect.AdjustBottom( 1 );
    }
 
    if (aFocusRect.Left()   < aOutputRect.Left())
        aFocusRect.SetLeft( aOutputRect.Left() );
    if (aFocusRect.Top()    < aOutputRect.Top())
        aFocusRect.SetTop( aOutputRect.Top() );
    if (aFocusRect.Right()  > aOutputRect.Right())
        aFocusRect.SetRight( aOutputRect.Right() );
    if (aFocusRect.Bottom() > aOutputRect.Bottom())
        aFocusRect.SetBottom( aOutputRect.Bottom() );
 
    mpButtonData->maFocusRect = aFocusRect;
}
 
const tools::Rectangle& Button::ImplGetFocusRect() const
{
    return mpButtonData->maFocusRect;
}
 
DrawButtonFlags& Button::GetButtonState()
{
    return mpButtonData->mnButtonState;
}
 
DrawButtonFlags Button::GetButtonState() const
{
    return mpButtonData->mnButtonState;
}
 
void Button::ImplSetSymbolAlign( SymbolAlign eAlign )
{
    if ( mpButtonData->meSymbolAlign != eAlign )
    {
        mpButtonData->meSymbolAlign = eAlign;
        StateChanged( StateChangedType::Data );
    }
}
 
void Button::SetSmallSymbol()
{
    mpButtonData->mbSmallSymbol = true;
}
 
bool Button::IsSmallSymbol () const
{
    return mpButtonData->mbSmallSymbol;
}
 
bool Button::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "image-position")
    {
        ImageAlign eAlign = ImageAlign::Left;
        if (rValue == "left")
            eAlign = ImageAlign::Left;
        else if (rValue == "right")
            eAlign = ImageAlign::Right;
        else if (rValue == "top")
            eAlign = ImageAlign::Top;
        else if (rValue == "bottom")
            eAlign = ImageAlign::Bottom;
        SetImageAlign(eAlign);
    }
    else if (rKey == "focus-on-click")
    {
        WinBits nBits = GetStyle();
        nBits &= ~WB_NOPOINTERFOCUS;
        if (!toBool(rValue))
            nBits |= WB_NOPOINTERFOCUS;
        SetStyle(nBits);
    }
    else
        return Control::set_property(rKey, rValue);
    return true;
}
 
void Button::statusChanged(const css::frame::FeatureStateEvent& rEvent)
{
    Enable(rEvent.IsEnabled);
}
 
FactoryFunction Button::GetUITestFactory() const
{
    return ButtonUIObject::create;
}
 
namespace
{
 
std::string_view symbolTypeName(SymbolType eSymbolType)
{
    switch (eSymbolType)
    {
        case SymbolType::DONTKNOW:         return "DONTKNOW";
        case SymbolType::IMAGE:            return "IMAGE";
        case SymbolType::ARROW_UP:         return "ARROW_UP";
        case SymbolType::ARROW_DOWN:       return "ARROW_DOWN";
        case SymbolType::ARROW_LEFT:       return "ARROW_LEFT";
        case SymbolType::ARROW_RIGHT:      return "ARROW_RIGHT";
        case SymbolType::SPIN_UP:          return "SPIN_UP";
        case SymbolType::SPIN_DOWN:        return "SPIN_DOWN";
        case SymbolType::SPIN_LEFT:        return "SPIN_LEFT";
        case SymbolType::SPIN_RIGHT:       return "SPIN_RIGHT";
        case SymbolType::FIRST:            return "FIRST";
        case SymbolType::LAST:             return "LAST";
        case SymbolType::PREV:             return "PREV";
        case SymbolType::NEXT:             return "NEXT";
        case SymbolType::PAGEUP:           return "PAGEUP";
        case SymbolType::PAGEDOWN:         return "PAGEDOWN";
        case SymbolType::PLAY:             return "PLAY";
        case SymbolType::STOP:             return "STOP";
        case SymbolType::CLOSE:            return "CLOSE";
        case SymbolType::CHECKMARK:        return "CHECKMARK";
        case SymbolType::RADIOCHECKMARK:   return "RADIOCHECKMARK";
        case SymbolType::FLOAT:            return "FLOAT";
        case SymbolType::DOCK:             return "DOCK";
        case SymbolType::HIDE:             return "HIDE";
        case SymbolType::HELP:             return "HELP";
        case SymbolType::PLUS:             return "PLUS";
    }
 
    return "UNKNOWN";
}
 
}
 
void Button::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    Control::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("text", GetText());
    if (HasImage())
    {
        SvMemoryStream aOStm(6535, 6535);
        if(GraphicConverter::Export(aOStm, GetModeImage().GetBitmapEx(), ConvertDataFormat::PNG) == ERRCODE_NONE)
        {
            css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
            OStringBuffer aBuffer("data:image/png;base64,");
            ::comphelper::Base64::encode(aBuffer, aSeq);
            rJsonWriter.put("image", aBuffer);
        }
    }
 
    if (GetStyle() & WB_DEFBUTTON)
        rJsonWriter.put("has_default", true);
}
 
void PushButton::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    Button::DumpAsPropertyTree(rJsonWriter);
    if (GetSymbol() != SymbolType::DONTKNOW)
        rJsonWriter.put("symbol", symbolTypeName(GetSymbol()));
    if (isToggleButton())
        rJsonWriter.put("isToggle", true);
}
 
IMPL_STATIC_LINK( Button, dispatchCommandHandler, Button*, pButton, void )
{
    if (pButton == nullptr)
        return;
 
    comphelper::dispatchCommand(pButton->maCommand, uno::Sequence<beans::PropertyValue>());
}
 
void PushButton::ImplInitPushButtonData()
{
    mpWindowImpl->mbPushButton    = true;
 
    meSymbol        = SymbolType::DONTKNOW;
    meState         = TRISTATE_FALSE;
    mnDDStyle       = PushButtonDropdownStyle::NONE;
    mbIsActive    = false;
    mbPressed       = false;
    mbIsAction      = false;
}
 
namespace
{
    vcl::Window* getPreviousSibling(vcl::Window const *pParent)
    {
        return pParent ? pParent->GetWindow(GetWindowType::LastChild) : nullptr;
    }
}
 
void PushButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
    Button::ImplInit( pParent, nStyle, nullptr );
 
    if ( nStyle & WB_NOLIGHTBORDER )
        GetButtonState() |= DrawButtonFlags::NoLightBorder;
 
    ImplInitSettings( true );
}
 
WinBits PushButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
{
    if ( !(nStyle & WB_NOTABSTOP) )
        nStyle |= WB_TABSTOP;
 
    // if no alignment is given, default to "vertically centered". This is because since
    // #i26046#, we respect the vertical alignment flags (previously we didn't completely),
    // but we of course want to look as before when no vertical alignment is specified
    if ( ( nStyle & ( WB_TOP | WB_VCENTER | WB_BOTTOM ) ) == 0 )
        nStyle |= WB_VCENTER;
 
    if ( !(nStyle & WB_NOGROUP) &&
         (!pPrevWindow ||
          ((pPrevWindow->GetType() != WindowType::PUSHBUTTON  ) &&
           (pPrevWindow->GetType() != WindowType::OKBUTTON    ) &&
           (pPrevWindow->GetType() != WindowType::CANCELBUTTON) &&
           (pPrevWindow->GetType() != WindowType::HELPBUTTON  )) ) )
        nStyle |= WB_GROUP;
    return nStyle;
}
 
const vcl::Font& PushButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
{
    return _rStyle.GetPushButtonFont();
}
 
const Color& PushButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
{
    return _rStyle.GetButtonTextColor();
}
 
void PushButton::ImplInitSettings( bool bBackground )
{
    Button::ImplInitSettings();
 
    if ( !bBackground )
        return;
 
    SetBackground();
    // #i38498#: do not check for GetParent()->IsChildTransparentModeEnabled()
    // otherwise the formcontrol button will be overdrawn due to ParentClipMode::NoClip
    // for radio and checkbox this is ok as they should appear transparent in documents
    if ( IsNativeControlSupported( ControlType::Pushbutton, ControlPart::Entire ) ||
         (GetStyle() & WB_FLATBUTTON) != 0 )
    {
        EnableChildTransparentMode();
        SetParentClipMode( ParentClipMode::NoClip );
        SetPaintTransparent( true );
 
        if ((GetStyle() & WB_FLATBUTTON) == 0)
            mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
        else
            mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRectsForFlatButtons;
    }
    else
    {
        EnableChildTransparentMode( false );
        SetParentClipMode();
        SetPaintTransparent( false );
    }
}
 
void PushButton::ImplDrawPushButtonFrame(vcl::RenderContext& rRenderContext,
                                         tools::Rectangle& rRect, DrawButtonFlags nStyle)
{
    if (!(GetStyle() & (WB_RECTSTYLE | WB_SMALLSTYLE)))
    {
        StyleSettings aStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
        if (IsControlBackground())
            aStyleSettings.Set3DColors(GetControlBackground());
    }
 
    DecorationView aDecoView(&rRenderContext);
    if (IsControlBackground())
    {
        AllSettings aSettings = rRenderContext.GetSettings();
        AllSettings aOldSettings = aSettings;
        StyleSettings aStyleSettings = aSettings.GetStyleSettings();
        if (nStyle & DrawButtonFlags::Highlight)
        {
            // with the custom background, native highlight do nothing, so code below mimic
            // native highlight by changing luminance
            Color controlBackgroundColorHighlighted = GetControlBackground();
            sal_uInt8 colorLuminance = controlBackgroundColorHighlighted.GetLuminance();
            if (colorLuminance < 205)
                controlBackgroundColorHighlighted.IncreaseLuminance(50);
            else
                controlBackgroundColorHighlighted.DecreaseLuminance(50);
            aStyleSettings.Set3DColors(controlBackgroundColorHighlighted);
        }
        else
            aStyleSettings.Set3DColors(GetControlBackground());
        aSettings.SetStyleSettings(aStyleSettings);
 
        // Call OutputDevice::SetSettings() explicitly, as rRenderContext may
        // be a vcl::Window in fact, and vcl::Window::SetSettings() will call
        // Invalidate(), which is a problem, since we're in Paint().
        rRenderContext.OutputDevice::SetSettings(aSettings);
        rRect = aDecoView.DrawButton(rRect, nStyle);
        rRenderContext.OutputDevice::SetSettings(aOldSettings);
    }
    else
        rRect = aDecoView.DrawButton(rRect, nStyle);
}
 
bool PushButton::ImplHitTestPushButton( vcl::Window const * pDev,
                                        const Point& rPos )
{
    tools::Rectangle   aTestRect( Point(), pDev->GetOutputSizePixel() );
 
    return aTestRect.Contains( rPos );
}
 
DrawTextFlags PushButton::ImplGetTextStyle( SystemTextColorFlags nSystemTextColorFlags ) const
{
    const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
 
    DrawTextFlags nTextStyle = DrawTextFlags::Mnemonic | DrawTextFlags::MultiLine | DrawTextFlags::EndEllipsis;
 
    if ( ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono ) ||
         ( nSystemTextColorFlags & SystemTextColorFlags::Mono ) )
        nTextStyle |= DrawTextFlags::Mono;
 
    if ( GetStyle() & WB_WORDBREAK )
        nTextStyle |= DrawTextFlags::WordBreak;
    if ( GetStyle() & WB_NOLABEL )
        nTextStyle &= ~DrawTextFlags::Mnemonic;
 
    if ( GetStyle() & WB_LEFT )
        nTextStyle |= DrawTextFlags::Left;
    else if ( GetStyle() & WB_RIGHT )
        nTextStyle |= DrawTextFlags::Right;
    else
        nTextStyle |= DrawTextFlags::Center;
 
    if ( GetStyle() & WB_TOP )
        nTextStyle |= DrawTextFlags::Top;
    else if ( GetStyle() & WB_BOTTOM )
        nTextStyle |= DrawTextFlags::Bottom;
    else
        nTextStyle |= DrawTextFlags::VCenter;
 
    if ( !IsEnabled() )
        nTextStyle |= DrawTextFlags::Disable;
 
    return nTextStyle;
}
 
void PushButton::ImplDrawPushButtonContent(OutputDevice *pDev, SystemTextColorFlags nSystemTextColorFlags,
                                           const tools::Rectangle &rRect, bool bMenuBtnSep,
                                           DrawButtonFlags nButtonFlags)
{
    const StyleSettings &rStyleSettings = GetSettings().GetStyleSettings();
    tools::Rectangle aInRect = rRect;
    Color aColor;
    DrawTextFlags nTextStyle = ImplGetTextStyle(nSystemTextColorFlags);
    DrawSymbolFlags nStyle;
 
    if (aInRect.Right() < aInRect.Left() || aInRect.Bottom() < aInRect.Top())
        return;
 
    pDev->Push(vcl::PushFlags::CLIPREGION);
    pDev->IntersectClipRegion(aInRect);
 
    if (nSystemTextColorFlags & SystemTextColorFlags::Mono)
        aColor = COL_BLACK;
 
    else if (IsControlForeground())
        aColor = GetControlForeground();
 
    // Button types with possibly different text coloring are flat buttons and regular buttons. Regular buttons may be action
    // buttons and may have an additional default status. Moreover all buttons may have an additional pressed and rollover
    // (highlight) status. Pressed buttons are always in rollover status.
 
    else if (GetStyle() & WB_FLATBUTTON)
        if (nButtonFlags & DrawButtonFlags::Pressed)
            aColor = rStyleSettings.GetFlatButtonPressedRolloverTextColor();
        else if (nButtonFlags & DrawButtonFlags::Highlight)
            aColor = rStyleSettings.GetFlatButtonRolloverTextColor();
        else
            aColor = rStyleSettings.GetFlatButtonTextColor();
    else
        if (isAction() && (nButtonFlags & DrawButtonFlags::Default))
            if (nButtonFlags & DrawButtonFlags::Pressed)
                aColor = rStyleSettings.GetDefaultActionButtonPressedRolloverTextColor();
            else if (nButtonFlags & DrawButtonFlags::Highlight)
                aColor = rStyleSettings.GetDefaultActionButtonRolloverTextColor();
            else
                aColor = rStyleSettings.GetDefaultActionButtonTextColor();
        else if (isAction())
            if (nButtonFlags & DrawButtonFlags::Pressed)
                aColor = rStyleSettings.GetActionButtonPressedRolloverTextColor();
            else if (nButtonFlags & DrawButtonFlags::Highlight)
                aColor = rStyleSettings.GetActionButtonRolloverTextColor();
            else
                aColor = rStyleSettings.GetActionButtonTextColor();
        else if (nButtonFlags & DrawButtonFlags::Default)
            if (nButtonFlags & DrawButtonFlags::Pressed)
                aColor = rStyleSettings.GetDefaultButtonPressedRolloverTextColor();
            else if (nButtonFlags & DrawButtonFlags::Highlight)
                aColor = rStyleSettings.GetDefaultButtonRolloverTextColor();
            else
                aColor = rStyleSettings.GetDefaultButtonTextColor();
        else
            if (nButtonFlags & DrawButtonFlags::Pressed)
                aColor = rStyleSettings.GetButtonPressedRolloverTextColor();
            else if (nButtonFlags & DrawButtonFlags::Highlight)
                aColor = rStyleSettings.GetButtonRolloverTextColor();
            else
                aColor = rStyleSettings.GetButtonTextColor();
 
#if defined(MACOSX) || defined(IOS)
    // tdf#152486 These are the buttons in infobars where the infobar has a custom
    // background color and on these platforms the buttons blend with
    // their background.
    vcl::Window* pParent = GetParent();
    if (pParent->get_id() == "ExtraButton")
    {
        while (pParent && !pParent->IsControlBackground())
            pParent = pParent->GetParent();
        if (pParent)
        {
            if (aColor.IsBright() && !pParent->GetControlBackground().IsDark())
                aColor = COL_BLACK;
        }
    }
#endif
 
    pDev->SetTextColor(aColor);
 
    if ( IsEnabled() )
        nStyle = DrawSymbolFlags::NONE;
    else
        nStyle = DrawSymbolFlags::Disable;
 
    Size aSize = rRect.GetSize();
    Point aPos = rRect.TopLeft();
 
    sal_Int32 nImageSep = 1 + (pDev->GetTextHeight()-10)/2;
    if( nImageSep < 1 )
        nImageSep = 1;
    if ( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
         mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
    {
        tools::Long nSeparatorX = 0;
        tools::Rectangle aSymbolRect = aInRect;
 
        // calculate symbol size
        tools::Long nSymbolSize    = pDev->GetTextHeight() / 2 + 1;
        if (nSymbolSize > aSize.Width() / 2)
            nSymbolSize = aSize.Width() / 2;
 
        nSeparatorX = aInRect.Right() - 2*nSymbolSize;
 
        // tdf#141761 Minimum width should be (1) Pixel, see comment
        // with same task number above for more info
        const tools::Long nWidthAdjust(2*nSymbolSize);
        aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
 
        // center symbol rectangle in the separated area
        aSymbolRect.AdjustRight( -(nSymbolSize/2) );
        aSymbolRect.SetLeft( aSymbolRect.Right() - nSymbolSize );
 
        ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
                              nTextStyle, nullptr, true );
 
        tools::Long nDistance = (aSymbolRect.GetHeight() > 10) ? 2 : 1;
        DecorationView aDecoView( pDev );
        if( bMenuBtnSep && nSeparatorX > 0 )
        {
            Point aStartPt( nSeparatorX, aSymbolRect.Top()+nDistance );
            Point aEndPt( nSeparatorX, aSymbolRect.Bottom()-nDistance );
            aDecoView.DrawSeparator( aStartPt, aEndPt );
        }
        ImplSetSeparatorX( nSeparatorX );
 
        aDecoView.DrawSymbol( aSymbolRect, SymbolType::SPIN_DOWN, aColor, nStyle );
 
    }
    else
    {
        tools::Rectangle aSymbolRect;
        ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
                              nTextStyle, IsSymbol() ? &aSymbolRect : nullptr, true );
 
        if ( IsSymbol() )
        {
            DecorationView aDecoView( pDev );
            aDecoView.DrawSymbol( aSymbolRect, meSymbol, aColor, nStyle );
        }
    }
 
    pDev->Pop();  // restore clipregion
}
 
void PushButton::ImplDrawPushButton(vcl::RenderContext& rRenderContext)
{
    HideFocus();
 
    DrawButtonFlags nButtonStyle = GetButtonState();
    Size aOutSz(GetOutputSizePixel());
    tools::Rectangle aRect(Point(), aOutSz);
    tools::Rectangle aInRect = aRect;
    bool bNativeOK = false;
 
    // adjust style if button should be rendered 'pressed'
    if (mbPressed || mbIsActive)
        nButtonStyle |= DrawButtonFlags::Pressed;
 
    if (GetStyle() & WB_FLATBUTTON)
        nButtonStyle |= DrawButtonFlags::Flat;
 
    // TODO: move this to Window class or make it a member !!!
    ControlType aCtrlType = ControlType::Generic;
    switch(GetParent()->GetType())
    {
        case WindowType::LISTBOX:
        case WindowType::MULTILISTBOX:
        case WindowType::TREELISTBOX:
            aCtrlType = ControlType::Listbox;
            break;
 
        case WindowType::COMBOBOX:
        case WindowType::PATTERNBOX:
        case WindowType::NUMERICBOX:
        case WindowType::METRICBOX:
        case WindowType::CURRENCYBOX:
        case WindowType::DATEBOX:
        case WindowType::TIMEBOX:
        case WindowType::LONGCURRENCYBOX:
            aCtrlType = ControlType::Combobox;
            break;
        default:
            break;
    }
 
    bool bDropDown = (IsSymbol() && (GetSymbol() == SymbolType::SPIN_DOWN) && GetText().isEmpty());
 
    if( bDropDown && (aCtrlType == ControlType::Combobox || aCtrlType == ControlType::Listbox))
    {
        if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::Entire))
        {
            // skip painting if the button was already drawn by the theme
            if (aCtrlType == ControlType::Combobox)
            {
                Edit* pEdit = static_cast<Edit*>(GetParent());
                if (pEdit->ImplUseNativeBorder(rRenderContext, pEdit->GetStyle()))
                    bNativeOK = true;
            }
            else if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::HasBackgroundTexture))
            {
                bNativeOK = true;
            }
 
            if (!bNativeOK && GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::ButtonDown))
            {
                // let the theme draw it, note we then need support
                // for ControlType::Listbox/ControlPart::ButtonDown and ControlType::Combobox/ControlPart::ButtonDown
 
                ImplControlValue aControlValue;
                ControlState nState = ControlState::NONE;
 
                if (mbPressed || mbIsActive)
                    nState |= ControlState::PRESSED;
                if (GetButtonState() & DrawButtonFlags::Pressed)
                    nState |= ControlState::PRESSED;
                if (HasFocus())
                    nState |= ControlState::FOCUSED;
                if (GetButtonState() & DrawButtonFlags::Default)
                    nState |= ControlState::DEFAULT;
                if (Window::IsEnabled())
                    nState |= ControlState::ENABLED;
 
                if (IsMouseOver() && aInRect.Contains(GetPointerPosPixel()))
                    nState |= ControlState::ROLLOVER;
 
                if ( IsMouseOver() && aInRect.Contains(GetPointerPosPixel()) && mbIsActive)
                {
                    nState |= ControlState::ROLLOVER;
                    nButtonStyle &= ~DrawButtonFlags::Pressed;
                }
 
                bNativeOK = rRenderContext.DrawNativeControl(aCtrlType, ControlPart::ButtonDown, aInRect, nState,
                                                             aControlValue, OUString());
            }
        }
    }
 
    if (bNativeOK)
        return;
 
    bool bRollOver = (IsMouseOver() && aInRect.Contains(GetPointerPosPixel()));
    if (bRollOver)
        nButtonStyle |= DrawButtonFlags::Highlight;
    bool bDrawMenuSep = mnDDStyle == PushButtonDropdownStyle::SplitMenuButton;
    if (GetStyle() & WB_FLATBUTTON)
    {
        if (!bRollOver && !HasFocus())
            bDrawMenuSep = false;
    }
    // tdf#123175 if there is a custom control bg set, draw the button without outsourcing to the NWF
    bNativeOK = !IsControlBackground() && rRenderContext.IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire);
    if (bNativeOK)
    {
        PushButtonValue aControlValue;
        aControlValue.mbIsAction = isAction();
 
        tools::Rectangle aCtrlRegion(aInRect);
        ControlState nState = ControlState::NONE;
 
        if (mbPressed || IsChecked() || mbIsActive)
        {
            nState |= ControlState::PRESSED;
            nButtonStyle |= DrawButtonFlags::Pressed;
        }
        if (GetButtonState() & DrawButtonFlags::Pressed)
            nState |= ControlState::PRESSED;
        if (HasFocus())
            nState |= ControlState::FOCUSED;
        if (GetButtonState() & DrawButtonFlags::Default)
            nState |= ControlState::DEFAULT;
        if (Window::IsEnabled())
            nState |= ControlState::ENABLED;
 
        if (bRollOver || mbIsActive)
        {
            nButtonStyle |= DrawButtonFlags::Highlight;
            nState |= ControlState::ROLLOVER;
        }
 
        if (mbIsActive && bRollOver)
        {
            nState &= ~ControlState::PRESSED;
            nButtonStyle &= ~DrawButtonFlags::Pressed;
        }
 
        if (GetStyle() & WB_FLATBUTTON)
            aControlValue.m_bFlatButton = true;
 
        // draw frame into invisible window to have aInRect modified correctly
        // but do not shift the inner rect for pressed buttons (ie remove DrawButtonFlags::Pressed)
        // this assumes the theme has enough visual cues to signalize the button was pressed
        //Window aWin( this );
        //ImplDrawPushButtonFrame( &aWin, aInRect, nButtonStyle & ~DrawButtonFlags::Pressed );
 
        // looks better this way as symbols were displaced slightly using the above approach
        aInRect.AdjustTop(4 );
        aInRect.AdjustBottom( -4 );
        aInRect.AdjustLeft(4 );
        aInRect.AdjustRight( -4 );
 
        // prepare single line hint (needed on mac to decide between normal push button and
        // rectangular bevel button look)
        Size aFontSize(Application::GetSettings().GetStyleSettings().GetPushButtonFont().GetFontSize());
        aFontSize = rRenderContext.LogicToPixel(aFontSize, MapMode(MapUnit::MapPoint));
        Size aInRectSize(rRenderContext.LogicToPixel(Size(aInRect.GetWidth(), aInRect.GetHeight())));
        aControlValue.mbSingleLine = (aInRectSize.Height() < 2 * aFontSize.Height());
 
        if (!aControlValue.m_bFlatButton || (nState & ControlState::ROLLOVER) || (nState & ControlState::PRESSED)
            || (HasFocus() && mpWindowImpl->mbUseNativeFocus
                && !IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus)))
        {
            bNativeOK = rRenderContext.DrawNativeControl(ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion, nState,
                                                         aControlValue, OUString() /*PushButton::GetText()*/);
        }
        else
        {
            bNativeOK = true;
        }
 
        // draw content using the same aInRect as non-native VCL would do
        ImplDrawPushButtonContent(&rRenderContext, SystemTextColorFlags::NONE,
                                  aInRect, bDrawMenuSep, nButtonStyle);
 
        if (HasFocus())
            ShowFocus(ImplGetFocusRect());
    }
 
    if (bNativeOK)
        return;
 
    // draw PushButtonFrame, aInRect has content size afterwards
    if (GetStyle() & WB_FLATBUTTON)
    {
        tools::Rectangle aTempRect(aInRect);
        ImplDrawPushButtonFrame(rRenderContext, aTempRect, nButtonStyle);
        aInRect.AdjustLeft(2 );
        aInRect.AdjustTop(2 );
        aInRect.AdjustRight( -2 );
        aInRect.AdjustBottom( -2 );
    }
    else
    {
        ImplDrawPushButtonFrame(rRenderContext, aInRect, nButtonStyle);
    }
 
    // draw content
    ImplDrawPushButtonContent(&rRenderContext, SystemTextColorFlags::NONE, aInRect, bDrawMenuSep, nButtonStyle);
 
    if (HasFocus())
    {
        ShowFocus(ImplGetFocusRect());
    }
}
 
void PushButton::ImplSetDefButton( bool bSet )
{
    Size aSize( GetSizePixel() );
    Point aPos( GetPosPixel() );
    int dLeft(0), dRight(0), dTop(0), dBottom(0);
    bool bSetPos = false;
 
    if ( IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
    {
        tools::Rectangle aBound, aCont;
        tools::Rectangle aCtrlRegion( 0, 0, 80, 20 ); // use a constant size to avoid accumulating
                                             // will not work if the theme has dynamic adornment sizes
        ImplControlValue aControlValue;
 
        // get native size of a 'default' button
        // and adjust the VCL button if more space for adornment is required
        if( GetNativeControlRegion( ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion,
                                    ControlState::DEFAULT|ControlState::ENABLED,
                                    aControlValue,
                                    aBound, aCont ) )
        {
            dLeft = aCont.Left() - aBound.Left();
            dTop = aCont.Top() - aBound.Top();
            dRight = aBound.Right() - aCont.Right();
            dBottom = aBound.Bottom() - aCont.Bottom();
            bSetPos = dLeft || dTop || dRight || dBottom;
        }
    }
 
    if ( bSet )
    {
        if( !(GetButtonState() & DrawButtonFlags::Default) && bSetPos )
        {
            // adjust pos/size when toggling from non-default to default
            aPos.Move(-dLeft, -dTop);
            aSize.AdjustWidth(dLeft + dRight );
            aSize.AdjustHeight(dTop + dBottom );
        }
        GetButtonState() |= DrawButtonFlags::Default;
    }
    else
    {
        if( (GetButtonState() & DrawButtonFlags::Default) && bSetPos )
        {
            // adjust pos/size when toggling from default to non-default
            aPos.Move(dLeft, dTop);
            aSize.AdjustWidth( -(dLeft + dRight) );
            aSize.AdjustHeight( -(dTop + dBottom) );
        }
        GetButtonState() &= ~DrawButtonFlags::Default;
    }
    if( bSetPos )
        setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
 
    Invalidate();
}
 
bool PushButton::ImplIsDefButton() const
{
    return bool(GetButtonState() & DrawButtonFlags::Default);
}
 
PushButton::PushButton( WindowType nType ) :
    Button( nType )
{
    ImplInitPushButtonData();
}
 
PushButton::PushButton( vcl::Window* pParent, WinBits nStyle ) :
    Button( WindowType::PUSHBUTTON )
{
    ImplInitPushButtonData();
    ImplInit( pParent, nStyle );
}
 
void PushButton::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( !(rMEvt.IsLeft() &&
         ImplHitTestPushButton( this, rMEvt.GetPosPixel() )) )
        return;
 
    StartTrackingFlags nTrackFlags = StartTrackingFlags::NONE;
 
    if ( ( GetStyle() & WB_REPEAT ) &&
         ! ( GetStyle() & WB_TOGGLE ) )
        nTrackFlags |= StartTrackingFlags::ButtonRepeat;
 
    GetButtonState() |= DrawButtonFlags::Pressed;
    Invalidate();
    StartTracking( nTrackFlags );
 
    if ( nTrackFlags & StartTrackingFlags::ButtonRepeat )
        Click();
}
 
void PushButton::Tracking( const TrackingEvent& rTEvt )
{
    if ( rTEvt.IsTrackingEnded() )
    {
        if ( GetButtonState() & DrawButtonFlags::Pressed )
        {
            if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
                GrabFocus();
 
            if ( GetStyle() & WB_TOGGLE )
            {
                // Don't toggle, when aborted
                if ( !rTEvt.IsTrackingCanceled() )
                {
                    if ( IsChecked() )
                    {
                        Check( false );
                        GetButtonState() &= ~DrawButtonFlags::Pressed;
                    }
                    else
                        Check();
                }
            }
            else
                GetButtonState() &= ~DrawButtonFlags::Pressed;
 
            Invalidate();
 
            // do not call Click handler if aborted
            if ( !rTEvt.IsTrackingCanceled() )
            {
                if ( ! ( GetStyle() & WB_REPEAT ) || ( GetStyle() & WB_TOGGLE ) )
                    Click();
            }
        }
    }
    else
    {
        if ( ImplHitTestPushButton( this, rTEvt.GetMouseEvent().GetPosPixel() ) )
        {
            if ( GetButtonState() & DrawButtonFlags::Pressed )
            {
                if ( rTEvt.IsTrackingRepeat() && (GetStyle() & WB_REPEAT) &&
                     ! ( GetStyle() & WB_TOGGLE ) )
                    Click();
            }
            else
            {
                GetButtonState() |= DrawButtonFlags::Pressed;
                Invalidate();
            }
        }
        else
        {
            if ( GetButtonState() & DrawButtonFlags::Pressed )
            {
                GetButtonState() &= ~DrawButtonFlags::Pressed;
                Invalidate();
            }
        }
    }
}
 
void PushButton::KeyInput( const KeyEvent& rKEvt )
{
    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
 
    if ( !aKeyCode.GetModifier() &&
         ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
    {
        if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
        {
            GetButtonState() |= DrawButtonFlags::Pressed;
            Invalidate();
        }
 
        if ( ( GetStyle() & WB_REPEAT ) &&
             ! ( GetStyle() & WB_TOGGLE ) )
            Click();
    }
    else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
    {
        GetButtonState() &= ~DrawButtonFlags::Pressed;
        Invalidate();
    }
    else
        Button::KeyInput( rKEvt );
}
 
void PushButton::KeyUp( const KeyEvent& rKEvt )
{
    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
 
    if ( (GetButtonState() & DrawButtonFlags::Pressed) &&
         ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
    {
        if ( GetStyle() & WB_TOGGLE )
        {
            if ( IsChecked() )
            {
                Check( false );
                GetButtonState() &= ~DrawButtonFlags::Pressed;
            }
            else
                Check();
 
            Toggle();
        }
        else
            GetButtonState() &= ~DrawButtonFlags::Pressed;
 
        Invalidate();
 
        if ( !( GetStyle() & WB_REPEAT ) || ( GetStyle() & WB_TOGGLE ) )
            Click();
    }
    else
        Button::KeyUp( rKEvt );
}
 
void PushButton::FillLayoutData() const
{
    mxLayoutData.emplace();
    const_cast<PushButton*>(this)->Invalidate();
}
 
void PushButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
{
    const Image& rCustomButtonImage = GetCustomButtonImage();
    if (!!rCustomButtonImage)
    {
        rRenderContext.DrawImage(Point(0, 0), rCustomButtonImage);
        return;
    }
    ImplDrawPushButton(rRenderContext);
}
 
void PushButton::Draw( OutputDevice* pDev, const Point& rPos,
                       SystemTextColorFlags nFlags )
{
    Point       aPos  = pDev->LogicToPixel( rPos );
    Size        aSize = GetSizePixel();
    tools::Rectangle   aRect( aPos, aSize );
    vcl::Font   aFont = GetDrawPixelFont( pDev );
 
    pDev->Push();
    pDev->SetMapMode();
    pDev->SetFont( aFont );
 
    std::optional<StyleSettings> oOrigDevStyleSettings;
 
    if ( nFlags & SystemTextColorFlags::Mono )
    {
        pDev->SetTextColor( COL_BLACK );
    }
    else
    {
        pDev->SetTextColor( GetTextColor() );
        // DecoView uses the FaceColor...
        AllSettings aSettings = pDev->GetSettings();
        StyleSettings aStyleSettings = aSettings.GetStyleSettings();
        oOrigDevStyleSettings = aStyleSettings;
        if ( IsControlBackground() )
            aStyleSettings.SetFaceColor( GetControlBackground() );
        else
            aStyleSettings.SetFaceColor( GetSettings().GetStyleSettings().GetFaceColor() );
        aSettings.SetStyleSettings( aStyleSettings );
        pDev->OutputDevice::SetSettings( aSettings );
    }
    pDev->SetTextFillColor();
 
    DecorationView aDecoView( pDev );
    DrawButtonFlags nButtonStyle = DrawButtonFlags::NONE;
    if ( nFlags & SystemTextColorFlags::Mono )
        nButtonStyle |= DrawButtonFlags::Mono;
    if ( IsChecked() )
        nButtonStyle |= DrawButtonFlags::Checked;
    aRect = aDecoView.DrawButton( aRect, nButtonStyle );
 
    ImplDrawPushButtonContent( pDev, nFlags, aRect, true, nButtonStyle );
 
    // restore original settings (which are not affected by Push/Pop) after
    // finished drawing
    if (oOrigDevStyleSettings)
    {
        AllSettings aSettings = pDev->GetSettings();
        aSettings.SetStyleSettings(*oOrigDevStyleSettings);
        pDev->OutputDevice::SetSettings( aSettings );
    }
 
    pDev->Pop();
}
 
void PushButton::Resize()
{
    Control::Resize();
    Invalidate();
}
 
void PushButton::GetFocus()
{
    ShowFocus( ImplGetFocusRect() );
    SetInputContext( InputContext( GetFont() ) );
    Button::GetFocus();
}
 
void PushButton::LoseFocus()
{
    EndSelection();
    HideFocus();
    Button::LoseFocus();
}
 
void PushButton::StateChanged( StateChangedType nType )
{
    Button::StateChanged( nType );
 
    if ( (nType == StateChangedType::Enable) ||
         (nType == StateChangedType::Text) ||
         (nType == StateChangedType::Data) ||
         (nType == StateChangedType::State) ||
         (nType == StateChangedType::UpdateMode) )
    {
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate();
    }
    else if ( nType == StateChangedType::Style )
    {
        SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
 
        bool bIsDefButton = ( GetStyle() & WB_DEFBUTTON ) != 0;
        bool bWasDefButton = ( GetPrevStyle() & WB_DEFBUTTON ) != 0;
        if ( bIsDefButton != bWasDefButton )
            ImplSetDefButton( bIsDefButton );
 
        if ( IsReallyVisible() && IsUpdateMode() )
        {
            if ( (GetPrevStyle() & PUSHBUTTON_VIEW_STYLE) !=
                 (GetStyle() & PUSHBUTTON_VIEW_STYLE) )
                Invalidate();
        }
    }
    else if ( (nType == StateChangedType::Zoom) ||
              (nType == StateChangedType::ControlFont) )
    {
        ImplInitSettings( false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlForeground )
    {
        ImplInitSettings( false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlBackground )
    {
        ImplInitSettings( true );
        Invalidate();
    }
}
 
void PushButton::DataChanged( const DataChangedEvent& rDCEvt )
{
    Button::DataChanged( rDCEvt );
 
    if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
         (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
         ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
    {
        ImplInitSettings( true );
        Invalidate();
    }
}
 
bool PushButton::PreNotify( NotifyEvent& rNEvt )
{
    if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
    {
        const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
        if( pMouseEvt && (pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow()) )
        {
            // trigger redraw as mouse over state has changed
 
            // TODO: move this to Window class or make it a member !!!
            ControlType aCtrlType = ControlType::Generic;
            switch( GetParent()->GetType() )
            {
                case WindowType::LISTBOX:
                case WindowType::MULTILISTBOX:
                case WindowType::TREELISTBOX:
                    aCtrlType = ControlType::Listbox;
                    break;
 
                case WindowType::COMBOBOX:
                case WindowType::PATTERNBOX:
                case WindowType::NUMERICBOX:
                case WindowType::METRICBOX:
                case WindowType::CURRENCYBOX:
                case WindowType::DATEBOX:
                case WindowType::TIMEBOX:
                case WindowType::LONGCURRENCYBOX:
                    aCtrlType = ControlType::Combobox;
                    break;
                default:
                    break;
            }
 
            bool bDropDown = ( IsSymbol() && (GetSymbol()==SymbolType::SPIN_DOWN) && GetText().isEmpty() );
 
            if( bDropDown && GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::Entire) &&
                   !GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::ButtonDown) )
            {
                vcl::Window *pBorder = GetParent()->GetWindow( GetWindowType::Border );
                if(aCtrlType == ControlType::Combobox)
                {
                    // only paint the button part to avoid flickering of the combobox text
                    tools::Rectangle aClipRect( Point(), GetOutputSizePixel() );
                    aClipRect.SetPos(pBorder->ScreenToOutputPixel(OutputToScreenPixel(aClipRect.TopLeft())));
                    pBorder->Invalidate( aClipRect );
                }
                else
                {
                    pBorder->Invalidate( InvalidateFlags::NoErase );
                }
            }
            else if( (GetStyle() & WB_FLATBUTTON) ||
                     IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
            {
                Invalidate();
            }
        }
    }
 
    return Button::PreNotify(rNEvt);
}
 
void PushButton::Toggle()
{
    ImplCallEventListenersAndHandler( VclEventId::PushbuttonToggle, nullptr );
}
 
void PushButton::SetSymbol( SymbolType eSymbol )
{
    if ( meSymbol != eSymbol )
    {
        meSymbol = eSymbol;
        CompatStateChanged( StateChangedType::Data );
    }
}
 
void PushButton::SetSymbolAlign( SymbolAlign eAlign )
{
    ImplSetSymbolAlign( eAlign );
}
 
void PushButton::SetDropDown( PushButtonDropdownStyle nStyle )
{
    if ( mnDDStyle != nStyle )
    {
        mnDDStyle = nStyle;
        CompatStateChanged( StateChangedType::Data );
    }
}
 
void PushButton::SetState( TriState eState )
{
    if ( meState == eState )
        return;
 
    meState = eState;
    if ( meState == TRISTATE_FALSE )
        GetButtonState() &= ~DrawButtonFlags(DrawButtonFlags::Checked | DrawButtonFlags::DontKnow);
    else if ( meState == TRISTATE_TRUE )
    {
        GetButtonState() &= ~DrawButtonFlags::DontKnow;
        GetButtonState() |= DrawButtonFlags::Checked;
    }
    else // TRISTATE_INDET
    {
        GetButtonState() &= ~DrawButtonFlags::Checked;
        GetButtonState() |= DrawButtonFlags::DontKnow;
    }
 
    CompatStateChanged( StateChangedType::State );
    Toggle();
}
 
void PushButton::statusChanged(const css::frame::FeatureStateEvent& rEvent)
{
    Button::statusChanged(rEvent);
    if (rEvent.State.has<bool>())
        SetPressed(rEvent.State.get<bool>());
}
 
void PushButton::SetPressed( bool bPressed )
{
    if ( mbPressed != bPressed )
    {
        mbPressed = bPressed;
        CompatStateChanged( StateChangedType::Data );
    }
}
 
void PushButton::EndSelection()
{
    EndTracking( TrackingEventFlags::Cancel );
    if ( !isDisposed() &&
         GetButtonState() & DrawButtonFlags::Pressed )
    {
        GetButtonState() &= ~DrawButtonFlags::Pressed;
        if ( !mbPressed )
            Invalidate();
    }
}
 
Size PushButton::CalcMinimumSize() const
{
    Size aSize;
 
    if ( IsSymbol() )
    {
        if ( IsSmallSymbol ())
            aSize = Size( 16, 12 );
        else
            aSize = Size( 26, 24 );
    }
    else if ( Button::HasImage() )
        aSize = GetModeImage().GetSizePixel();
    if( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
        mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
    {
        tools::Long nSymbolSize = GetTextHeight() / 2 + 1;
        aSize.AdjustWidth(2*nSymbolSize );
    }
    if (!PushButton::GetText().isEmpty())
    {
        Size textSize = GetTextRect( tools::Rectangle( Point(), Size( 0x7fffffff, 0x7fffffff ) ),
                                     PushButton::GetText(), ImplGetTextStyle( SystemTextColorFlags::NONE ) ).GetSize();
 
        tools::Long nTextHeight = textSize.Height() * 1.15;
 
        ImageAlign eImageAlign = GetImageAlign();
        // tdf#142337 only considering the simple top/bottom/left/right possibilities
        if (eImageAlign == ImageAlign::Top || eImageAlign == ImageAlign::Bottom)
        {
            aSize.AdjustHeight(nTextHeight);
            aSize.setWidth(std::max(aSize.Width(), textSize.Width()));
        }
        else
        {
            aSize.AdjustWidth(textSize.Width());
            aSize.setHeight(std::max(aSize.Height(), nTextHeight));
        }
    }
 
    // cf. ImplDrawPushButton ...
    if( (GetStyle() & WB_SMALLSTYLE) == 0 )
    {
        aSize.AdjustWidth(24 );
        aSize.AdjustHeight(12 );
    }
 
    return CalcWindowSize( aSize );
}
 
Size PushButton::GetOptimalSize() const
{
    return CalcMinimumSize();
}
 
bool PushButton::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "has-default")
    {
        WinBits nBits = GetStyle();
        nBits &= ~WB_DEFBUTTON;
        if (toBool(rValue))
            nBits |= WB_DEFBUTTON;
        SetStyle(nBits);
    }
    else
        return Button::set_property(rKey, rValue);
    return true;
}
 
void PushButton::ShowFocus(const tools::Rectangle& rRect)
{
    if (IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
    {
        PushButtonValue aControlValue;
        aControlValue.mbIsAction = isAction();
        tools::Rectangle aInRect(Point(), GetOutputSizePixel());
        GetOutDev()->DrawNativeControl(ControlType::Pushbutton, ControlPart::Focus, aInRect,
                                       ControlState::FOCUSED, aControlValue, OUString());
    }
    Button::ShowFocus(rRect);
}
 
void OKButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    set_id(u"ok"_ustr);
    PushButton::ImplInit( pParent, nStyle );
 
    SetText( GetStandardText( StandardButtonType::OK ) );
}
 
OKButton::OKButton( vcl::Window* pParent, WinBits nStyle ) :
    PushButton( WindowType::OKBUTTON )
{
    ImplInit( pParent, nStyle );
}
 
void OKButton::Click()
{
    // close parent if no link set
    if ( !GetClickHdl() )
    {
        vcl::Window* pParent = getNonLayoutParent(this);
        if ( pParent->IsSystemWindow() )
        {
            if ( pParent->IsDialog() )
            {
                VclPtr<Dialog> xParent( static_cast<Dialog*>(pParent) );
                if ( xParent->IsInExecute() )
                    xParent->EndDialog( RET_OK );
                // prevent recursive calls
                else if ( !xParent->IsInClose() )
                {
                    if ( pParent->GetStyle() & WB_CLOSEABLE )
                        xParent->Close();
                }
            }
            else
            {
                if ( pParent->GetStyle() & WB_CLOSEABLE )
                    static_cast<SystemWindow*>(pParent)->Close();
            }
        }
    }
    else
    {
        PushButton::Click();
    }
}
 
void CancelButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    set_id(u"cancel"_ustr);
    PushButton::ImplInit( pParent, nStyle );
 
    SetText( GetStandardText( StandardButtonType::Cancel ) );
}
 
CancelButton::CancelButton( vcl::Window* pParent, WinBits nStyle ) :
    PushButton( WindowType::CANCELBUTTON )
{
    ImplInit( pParent, nStyle );
}
 
void CancelButton::Click()
{
    // close parent if link not set
    if ( !GetClickHdl() )
    {
        vcl::Window* pParent = getNonLayoutParent(this);
        if ( pParent->IsSystemWindow() )
        {
            if ( pParent->IsDialog() )
            {
                if ( static_cast<Dialog*>(pParent)->IsInExecute() )
                    static_cast<Dialog*>(pParent)->EndDialog();
                // prevent recursive calls
                else if ( !static_cast<Dialog*>(pParent)->IsInClose() )
                {
                    if ( pParent->GetStyle() & WB_CLOSEABLE )
                        static_cast<Dialog*>(pParent)->Close();
                }
            }
            else
            {
                if ( pParent->GetStyle() & WB_CLOSEABLE )
                    static_cast<SystemWindow*>(pParent)->Close();
            }
        }
    }
    else
    {
        PushButton::Click();
    }
}
 
CloseButton::CloseButton( vcl::Window* pParent )
    : CancelButton(pParent, 0)
{
    SetText( GetStandardText( StandardButtonType::Close ) );
}
 
void HelpButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    set_id(u"help"_ustr);
    PushButton::ImplInit( pParent, nStyle | WB_NOPOINTERFOCUS );
 
    SetText( GetStandardText( StandardButtonType::Help ) );
}
 
HelpButton::HelpButton( vcl::Window* pParent, WinBits nStyle ) :
    PushButton( WindowType::HELPBUTTON )
{
    ImplInit( pParent, nStyle );
}
 
void HelpButton::Click()
{
    // trigger help if no link set
    if ( !GetClickHdl() )
    {
        vcl::Window* pFocusWin = Application::GetFocusWindow();
        if ( !pFocusWin || comphelper::LibreOfficeKit::isActive() )
            pFocusWin = this;
 
        HelpEvent aEvt( pFocusWin->GetPointerPosPixel(), HelpEventMode::CONTEXT );
        pFocusWin->RequestHelp( aEvt );
    }
    PushButton::Click();
}
 
void HelpButton::StateChanged( StateChangedType nStateChange )
{
    // Hide when we have no help URL.
    if (comphelper::LibreOfficeKit::isActive() &&
        officecfg::Office::Common::Help::HelpRootURL::get().isEmpty())
        Hide();
    else
        PushButton::StateChanged(nStateChange);
}
 
void RadioButton::ImplInitRadioButtonData()
{
    mbChecked       = false;
    mbRadioCheck    = true;
    mbStateChanged  = false;
}
 
void RadioButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
    Button::ImplInit( pParent, nStyle, nullptr );
 
    ImplInitSettings( true );
}
 
WinBits RadioButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle ) const
{
    if ( !(nStyle & WB_NOGROUP) &&
         (!pPrevWindow || (pPrevWindow->GetType() != WindowType::RADIOBUTTON)) )
        nStyle |= WB_GROUP;
    if ( !(nStyle & WB_NOTABSTOP) )
    {
        if ( IsChecked() )
            nStyle |= WB_TABSTOP;
        else
            nStyle &= ~WB_TABSTOP;
    }
 
    return nStyle;
}
 
const vcl::Font& RadioButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
{
    return _rStyle.GetRadioCheckFont();
}
 
const Color& RadioButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
{
    return _rStyle.GetRadioCheckTextColor();
}
 
void RadioButton::ImplInitSettings( bool bBackground )
{
    Button::ImplInitSettings();
 
    if ( !bBackground )
        return;
 
    vcl::Window* pParent = GetParent();
    if ( !IsControlBackground() &&
        (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) ) )
    {
        EnableChildTransparentMode();
        SetParentClipMode( ParentClipMode::NoClip );
        SetPaintTransparent( true );
        SetBackground();
        if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
            mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
    }
    else
    {
        EnableChildTransparentMode( false );
        SetParentClipMode();
        SetPaintTransparent( false );
 
        if ( IsControlBackground() )
            SetBackground( GetControlBackground() );
        else
            SetBackground( pParent->GetBackground() );
    }
}
 
void RadioButton::ImplDrawRadioButtonState(vcl::RenderContext& rRenderContext)
{
    bool bNativeOK = false;
 
    // no native drawing for image radio buttons
    if (!maImage && rRenderContext.IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire))
    {
        ImplControlValue aControlValue( mbChecked ? ButtonValue::On : ButtonValue::Off );
        tools::Rectangle aCtrlRect(maStateRect.TopLeft(), maStateRect.GetSize());
        ControlState nState = ControlState::NONE;
 
        if (GetButtonState() & DrawButtonFlags::Pressed)
            nState |= ControlState::PRESSED;
        if (HasFocus())
            nState |= ControlState::FOCUSED;
        if (GetButtonState() & DrawButtonFlags::Default)
            nState |= ControlState::DEFAULT;
        if (IsEnabled())
            nState |= ControlState::ENABLED;
 
        if (IsMouseOver() && maMouseRect.Contains(GetPointerPosPixel()))
            nState |= ControlState::ROLLOVER;
 
        bNativeOK = rRenderContext.DrawNativeControl(ControlType::Radiobutton, ControlPart::Entire, aCtrlRect,
                                                     nState, aControlValue, OUString());
    }
 
    if (bNativeOK)
        return;
 
    if (!maImage)
    {
        DrawButtonFlags nStyle = GetButtonState();
        if (!IsEnabled())
            nStyle |= DrawButtonFlags::Disabled;
        if (mbChecked)
            nStyle |= DrawButtonFlags::Checked;
        Image aImage = GetRadioImage(rRenderContext.GetSettings(), nStyle);
        if (IsZoom())
            rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
        else
            rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
    }
    else
    {
        HideFocus();
 
        DecorationView aDecoView(&rRenderContext);
        const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
        tools::Rectangle aImageRect  = maStateRect;
        Size aImageSize = maImage.GetSizePixel();
        bool bEnabled = IsEnabled();
 
        aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
        aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
 
        aImageRect.AdjustLeft( 1 );
        aImageRect.AdjustTop( 1 );
        aImageRect.AdjustRight( -1 );
        aImageRect.AdjustBottom( -1 );
 
        // display border and selection status
        aImageRect = aDecoView.DrawFrame(aImageRect, DrawFrameStyle::DoubleIn);
        if ((GetButtonState() & DrawButtonFlags::Pressed) || !bEnabled)
            rRenderContext.SetFillColor( rStyleSettings.GetFaceColor());
        else
            rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
        rRenderContext.SetLineColor();
        rRenderContext.DrawRect(aImageRect);
 
        // display image
        DrawImageFlags nImageStyle = DrawImageFlags::NONE;
        if (!bEnabled)
            nImageStyle |= DrawImageFlags::Disable;
 
        Image* pImage = &maImage;
 
        Point aImagePos(aImageRect.TopLeft());
        aImagePos.AdjustX((aImageRect.GetWidth() - aImageSize.Width()) / 2 );
        aImagePos.AdjustY((aImageRect.GetHeight() - aImageSize.Height()) / 2 );
        if (IsZoom())
            rRenderContext.DrawImage(aImagePos, aImageSize, *pImage, nImageStyle);
        else
            rRenderContext.DrawImage(aImagePos, *pImage, nImageStyle);
 
        aImageRect.AdjustLeft( 1 );
        aImageRect.AdjustTop( 1 );
        aImageRect.AdjustRight( -1 );
        aImageRect.AdjustBottom( -1 );
 
        ImplSetFocusRect(aImageRect);
 
        if (mbChecked)
        {
            rRenderContext.SetLineColor(rStyleSettings.GetHighlightColor());
            rRenderContext.SetFillColor();
            if ((aImageSize.Width() >= 20) || (aImageSize.Height() >= 20))
            {
                aImageRect.AdjustLeft( 1 );
                aImageRect.AdjustTop( 1 );
                aImageRect.AdjustRight( -1 );
                aImageRect.AdjustBottom( -1 );
            }
            rRenderContext.DrawRect(aImageRect);
            aImageRect.AdjustLeft( 1 );
            aImageRect.AdjustTop( 1 );
            aImageRect.AdjustRight( -1 );
            aImageRect.AdjustBottom( -1 );
            rRenderContext.DrawRect(aImageRect);
        }
 
        if (HasFocus())
            ShowFocus(ImplGetFocusRect());
    }
}
 
// for drawing RadioButton or CheckButton that has Text and/or Image
void Button::ImplDrawRadioCheck(OutputDevice* pDev, WinBits nWinStyle, SystemTextColorFlags nSystemTextColorFlags,
                                const Point& rPos, const Size& rSize,
                                const Size& rImageSize, tools::Rectangle& rStateRect,
                                tools::Rectangle& rMouseRect)
{
    DrawTextFlags nTextStyle = Button::ImplGetTextStyle( nWinStyle, nSystemTextColorFlags );
 
    const tools::Long nImageSep = GetDrawPixel( pDev, ImplGetImageToTextDistance() );
    Size aSize( rSize );
    Point aPos( rPos );
    aPos.AdjustX(rImageSize.Width() + nImageSep );
 
    // tdf#141761 Old (convenience?) adjustment of width may lead to empty
    // or negative(!) Size, that needs to be avoided. The coordinate context
    // is pixel-oriented (all Paints of Controls are, historically), so
    // the minimum width should be '1' Pixel.
    // Hint: nImageSep is based on Zoom (using Window::CalcZoom) and
    // MapModes (using Window::GetDrawPixel) - so potentially a wide range
    // of unpredictable values is possible
    const tools::Long nWidthAdjust(rImageSize.Width() + nImageSep);
    aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
 
    // if the text rect height is smaller than the height of the image
    // then for single lines the default should be centered text
    if( (nWinStyle & (WB_TOP|WB_VCENTER|WB_BOTTOM)) == 0 &&
        (rImageSize.Height() > rSize.Height() || ! (nWinStyle & WB_WORDBREAK) ) )
    {
        nTextStyle &= ~DrawTextFlags(DrawTextFlags::Top|DrawTextFlags::Bottom);
        nTextStyle |= DrawTextFlags::VCenter;
        aSize.setHeight( rImageSize.Height() );
    }
 
    ImplDrawAlignedImage( pDev, aPos, aSize, 1, nTextStyle );
 
    rMouseRect = tools::Rectangle( aPos, aSize );
    rMouseRect.SetLeft( rPos.X() );
 
    rStateRect.SetLeft( rPos.X() );
    rStateRect.SetTop( rMouseRect.Top() );
 
    if ( aSize.Height() > rImageSize.Height() )
        rStateRect.AdjustTop(( aSize.Height() - rImageSize.Height() ) / 2 );
    else
    {
        rStateRect.AdjustTop( -(( rImageSize.Height() - aSize.Height() ) / 2) );
        if( rStateRect.Top() < 0 )
            rStateRect.SetTop( 0 );
    }
 
    rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
    rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
 
    if ( rStateRect.Bottom() > rMouseRect.Bottom() )
        rMouseRect.SetBottom( rStateRect.Bottom() );
}
 
void RadioButton::ImplDraw( OutputDevice* pDev, SystemTextColorFlags nSystemTextColorFlags,
                            const Point& rPos, const Size& rSize,
                            const Size& rImageSize, tools::Rectangle& rStateRect,
                            tools::Rectangle& rMouseRect )
{
    WinBits                 nWinStyle = GetStyle();
    OUString                aText( GetText() );
 
    pDev->Push( vcl::PushFlags::CLIPREGION );
    pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
 
    // no image radio button
    if ( !maImage )
    {
        if (!aText.isEmpty() || HasImage())
        {
            Button::ImplDrawRadioCheck(pDev, nWinStyle, nSystemTextColorFlags,
                                       rPos, rSize, rImageSize,
                                       rStateRect, rMouseRect);
        }
        else
        {
            rStateRect.SetLeft( rPos.X() );
            if ( nWinStyle & WB_VCENTER )
                rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
            else if ( nWinStyle & WB_BOTTOM )
                rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() ); //-1;
            else
                rStateRect.SetTop( rPos.Y() );
            rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
            rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
            rMouseRect          = rStateRect;
 
            ImplSetFocusRect( rStateRect );
        }
    }
    else
    {
        bool        bTopImage   = (nWinStyle & WB_TOP) != 0;
        Size        aImageSize  = maImage.GetSizePixel();
        tools::Rectangle   aImageRect( rPos, rSize );
        tools::Long        nTextHeight = pDev->GetTextHeight();
        tools::Long        nTextWidth  = pDev->GetCtrlTextWidth( aText );
 
        // calculate position and sizes
        if (!aText.isEmpty())
        {
            Size aTmpSize( (aImageSize.Width()+8), (aImageSize.Height()+8) );
            if ( bTopImage )
            {
                aImageRect.SetLeft( (rSize.Width()-aTmpSize.Width())/2 );
                aImageRect.SetTop( (rSize.Height()-(aTmpSize.Height()+nTextHeight+6))/2 );
            }
            else
                aImageRect.SetTop( (rSize.Height()-aTmpSize.Height())/2 );
 
            aImageRect.SetRight( aImageRect.Left()+aTmpSize.Width() );
            aImageRect.SetBottom( aImageRect.Top()+aTmpSize.Height() );
 
            // display text
            Point aTxtPos = rPos;
            if ( bTopImage )
            {
                aTxtPos.AdjustX((rSize.Width()-nTextWidth)/2 );
                aTxtPos.AdjustY(aImageRect.Bottom()+6 );
            }
            else
            {
                aTxtPos.AdjustX(aImageRect.Right()+8 );
                aTxtPos.AdjustY((rSize.Height()-nTextHeight)/2 );
            }
            pDev->DrawCtrlText( aTxtPos, aText, 0, aText.getLength() );
        }
 
        rMouseRect = aImageRect;
        rStateRect = aImageRect;
    }
 
    pDev->Pop();
}
 
void RadioButton::ImplDrawRadioButton(vcl::RenderContext& rRenderContext)
{
    HideFocus();
 
    Size aImageSize;
    if (!maImage)
        aImageSize = ImplGetRadioImageSize();
    else
        aImageSize  = maImage.GetSizePixel();
 
    aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
    aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
 
    // Draw control text
    ImplDraw(&rRenderContext, SystemTextColorFlags::NONE, Point(), GetOutputSizePixel(),
             aImageSize, maStateRect, maMouseRect);
 
    if (!maImage && HasFocus())
        ShowFocus(ImplGetFocusRect());
 
    ImplDrawRadioButtonState(rRenderContext);
}
 
void RadioButton::group(RadioButton &rOther)
{
    if (&rOther == this)
        return;
 
    if (!m_xGroup)
    {
        m_xGroup = std::make_shared<std::vector<VclPtr<RadioButton> >>();
        m_xGroup->push_back(this);
    }
 
    auto aFind = std::find(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(&rOther));
    if (aFind == m_xGroup->end())
    {
        m_xGroup->push_back(&rOther);
 
        if (rOther.m_xGroup)
        {
            std::vector< VclPtr<RadioButton> > aOthers(rOther.GetRadioButtonGroup(false));
            //make all members of the group share the same button group
            for (auto const& elem : aOthers)
            {
                aFind = std::find(m_xGroup->begin(), m_xGroup->end(), elem);
                if (aFind == m_xGroup->end())
                    m_xGroup->push_back(elem);
            }
        }
 
        //make all members of the group share the same button group
        for (VclPtr<RadioButton> const & pButton : *m_xGroup)
        {
            pButton->m_xGroup = m_xGroup;
        }
    }
 
    //if this one is checked, uncheck all the others
    if (mbChecked)
        ImplUncheckAllOther();
}
 
std::vector< VclPtr<RadioButton> > RadioButton::GetRadioButtonGroup(bool bIncludeThis) const
{
    if (m_xGroup)
    {
        if (bIncludeThis)
            return *m_xGroup;
        std::vector< VclPtr<RadioButton> > aGroup;
        for (VclPtr<RadioButton> const & pRadioButton : *m_xGroup)
        {
            if (pRadioButton == this)
                continue;
            aGroup.push_back(pRadioButton);
        }
        return aGroup;
    }
 
    std::vector<VclPtr<RadioButton>> aGroup;
    if (mbUsesExplicitGroup)
        return aGroup;
 
    //old-school
 
    // go back to first in group;
    vcl::Window* pFirst = const_cast<RadioButton*>(this);
    while( ( pFirst->GetStyle() & WB_GROUP ) == 0 )
    {
        vcl::Window* pWindow = pFirst->GetWindow( GetWindowType::Prev );
        if( pWindow )
            pFirst = pWindow;
        else
            break;
    }
    // insert radiobuttons up to next group
    do
    {
        if( pFirst->GetType() == WindowType::RADIOBUTTON )
        {
            if( pFirst != this || bIncludeThis )
                aGroup.emplace_back(static_cast<RadioButton*>(pFirst) );
        }
        pFirst = pFirst->GetWindow( GetWindowType::Next );
    } while( pFirst && ( ( pFirst->GetStyle() & WB_GROUP ) == 0 ) );
 
    return aGroup;
}
 
void RadioButton::ImplUncheckAllOther()
{
    mpWindowImpl->mnStyle |= WB_TABSTOP;
 
    std::vector<VclPtr<RadioButton> > aGroup(GetRadioButtonGroup(false));
    // iterate over radio button group and checked buttons
    for (VclPtr<RadioButton>& pWindow : aGroup)
    {
        if ( pWindow->IsChecked() )
        {
            pWindow->SetState( false );
            if ( pWindow->isDisposed() )
                return;
        }
 
        // not inside if clause to always remove wrongly set WB_TABSTOPS
        pWindow->mpWindowImpl->mnStyle &= ~WB_TABSTOP;
    }
}
 
void RadioButton::ImplCallClick( bool bGrabFocus, GetFocusFlags nFocusFlags )
{
    mbStateChanged = !mbChecked;
    mbChecked = true;
    mpWindowImpl->mnStyle |= WB_TABSTOP;
    Invalidate();
    VclPtr<vcl::Window> xWindow = this;
    if ( mbRadioCheck )
        ImplUncheckAllOther();
    if ( xWindow->isDisposed() )
        return;
    if ( bGrabFocus )
        ImplGrabFocus( nFocusFlags );
    if ( xWindow->isDisposed() )
        return;
    if ( mbStateChanged )
        Toggle();
    if ( xWindow->isDisposed() )
        return;
    Click();
    if ( xWindow->isDisposed() )
        return;
    mbStateChanged = false;
}
 
RadioButton::RadioButton(vcl::Window* pParent, bool bUsesExplicitGroup, WinBits nStyle)
    : Button(WindowType::RADIOBUTTON)
    , mbUsesExplicitGroup(bUsesExplicitGroup)
{
    ImplInitRadioButtonData();
    ImplInit( pParent, nStyle );
}
 
RadioButton::~RadioButton()
{
    disposeOnce();
}
 
void RadioButton::dispose()
{
    if (m_xGroup)
    {
        std::erase(*m_xGroup, VclPtr<RadioButton>(this));
        m_xGroup.reset();
    }
    Button::dispose();
}
 
void RadioButton::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( rMEvt.IsLeft() && maMouseRect.Contains( rMEvt.GetPosPixel() ) )
    {
        GetButtonState() |= DrawButtonFlags::Pressed;
        Invalidate();
        StartTracking();
        return;
    }
 
    Button::MouseButtonDown( rMEvt );
}
 
void RadioButton::Tracking( const TrackingEvent& rTEvt )
{
    if ( rTEvt.IsTrackingEnded() )
    {
        if ( GetButtonState() & DrawButtonFlags::Pressed )
        {
            if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
                GrabFocus();
 
            GetButtonState() &= ~DrawButtonFlags::Pressed;
 
            // do not call click handler if aborted
            if ( !rTEvt.IsTrackingCanceled() )
                ImplCallClick();
            else
            {
                Invalidate();
            }
        }
    }
    else
    {
        if ( maMouseRect.Contains( rTEvt.GetMouseEvent().GetPosPixel() ) )
        {
            if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
            {
                GetButtonState() |= DrawButtonFlags::Pressed;
                Invalidate();
            }
        }
        else
        {
            if ( GetButtonState() & DrawButtonFlags::Pressed )
            {
                GetButtonState() &= ~DrawButtonFlags::Pressed;
                Invalidate();
            }
        }
    }
}
 
void RadioButton::KeyInput( const KeyEvent& rKEvt )
{
    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
 
    if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
    {
        if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
        {
            GetButtonState() |= DrawButtonFlags::Pressed;
            Invalidate();
        }
    }
    else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
    {
        GetButtonState() &= ~DrawButtonFlags::Pressed;
        Invalidate();
    }
    else
        Button::KeyInput( rKEvt );
}
 
void RadioButton::KeyUp( const KeyEvent& rKEvt )
{
    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
 
    if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
    {
        GetButtonState() &= ~DrawButtonFlags::Pressed;
        ImplCallClick();
    }
    else
        Button::KeyUp( rKEvt );
}
 
void RadioButton::FillLayoutData() const
{
    mxLayoutData.emplace();
    const_cast<RadioButton*>(this)->Invalidate();
}
 
void RadioButton::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
{
    ImplDrawRadioButton(rRenderContext);
}
 
void RadioButton::Draw( OutputDevice* pDev, const Point& rPos,
                        SystemTextColorFlags nFlags )
{
    if ( !maImage )
    {
        MapMode     aResMapMode( MapUnit::Map100thMM );
        Point       aPos  = pDev->LogicToPixel( rPos );
        Size        aSize = GetSizePixel();
        Size        aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
        Size        aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
        Size        aBrd2Size = pDev->LogicToPixel( Size( 60, 60 ), aResMapMode );
        vcl::Font   aFont = GetDrawPixelFont( pDev );
        tools::Rectangle   aStateRect;
        tools::Rectangle   aMouseRect;
 
        aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
        aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
        aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
        aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
        aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
        aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
 
        if ( !aBrd1Size.Width() )
            aBrd1Size.setWidth( 1 );
        if ( !aBrd1Size.Height() )
            aBrd1Size.setHeight( 1 );
        if ( !aBrd2Size.Width() )
            aBrd2Size.setWidth( 1 );
        if ( !aBrd2Size.Height() )
            aBrd2Size.setHeight( 1 );
 
        pDev->Push();
        pDev->SetMapMode();
        pDev->SetFont( aFont );
        if ( nFlags & SystemTextColorFlags::Mono )
            pDev->SetTextColor( COL_BLACK );
        else
            pDev->SetTextColor( GetTextColor() );
        pDev->SetTextFillColor();
 
        ImplDraw( pDev, nFlags, aPos, aSize,
                  aImageSize, aStateRect, aMouseRect );
 
        Point   aCenterPos = aStateRect.Center();
        tools::Long    nRadX = aImageSize.Width()/2;
        tools::Long    nRadY = aImageSize.Height()/2;
 
        pDev->SetLineColor();
        pDev->SetFillColor( COL_BLACK );
        pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
        nRadX -= aBrd1Size.Width();
        nRadY -= aBrd1Size.Height();
        pDev->SetFillColor( COL_WHITE );
        pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
        if ( mbChecked )
        {
            nRadX -= aBrd1Size.Width();
            nRadY -= aBrd1Size.Height();
            if ( !nRadX )
                nRadX = 1;
            if ( !nRadY )
                nRadY = 1;
            pDev->SetFillColor( COL_BLACK );
            pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
        }
 
        pDev->Pop();
    }
    else
    {
        OSL_FAIL( "RadioButton::Draw() - not implemented for RadioButton with Image" );
    }
}
 
void RadioButton::Resize()
{
    Control::Resize();
    Invalidate();
}
 
void RadioButton::GetFocus()
{
    ShowFocus( ImplGetFocusRect() );
    SetInputContext( InputContext( GetFont() ) );
    Button::GetFocus();
}
 
void RadioButton::LoseFocus()
{
    if ( GetButtonState() & DrawButtonFlags::Pressed )
    {
        GetButtonState() &= ~DrawButtonFlags::Pressed;
        Invalidate();
    }
 
    HideFocus();
    Button::LoseFocus();
}
 
void RadioButton::StateChanged( StateChangedType nType )
{
    Button::StateChanged( nType );
 
    if ( nType == StateChangedType::State )
    {
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate( maStateRect );
    }
    else if ( (nType == StateChangedType::Enable) ||
              (nType == StateChangedType::Text) ||
              (nType == StateChangedType::Data) ||
              (nType == StateChangedType::UpdateMode) )
    {
        if ( IsUpdateMode() )
            Invalidate();
    }
    else if ( nType == StateChangedType::Style )
    {
        SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
 
        if ( (GetPrevStyle() & RADIOBUTTON_VIEW_STYLE) !=
             (GetStyle() & RADIOBUTTON_VIEW_STYLE) )
        {
            if ( IsUpdateMode() )
                Invalidate();
        }
    }
    else if ( (nType == StateChangedType::Zoom) ||
              (nType == StateChangedType::ControlFont) )
    {
        ImplInitSettings( false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlForeground )
    {
        ImplInitSettings( false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlBackground )
    {
        ImplInitSettings( true );
        Invalidate();
    }
}
 
void RadioButton::DataChanged( const DataChangedEvent& rDCEvt )
{
    Button::DataChanged( rDCEvt );
 
    if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
         (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
         ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
    {
        ImplInitSettings( true );
        Invalidate();
    }
}
 
bool RadioButton::PreNotify( NotifyEvent& rNEvt )
{
    if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
    {
        const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
        if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
        {
            // trigger redraw if mouse over state has changed
            if( IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire) )
            {
                if (maMouseRect.Contains(GetPointerPosPixel()) != maMouseRect.Contains(GetLastPointerPosPixel()) ||
                    pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
                {
                    Invalidate( maStateRect );
                }
            }
        }
    }
 
    return Button::PreNotify(rNEvt);
}
 
void RadioButton::Toggle()
{
    ImplCallEventListenersAndHandler( VclEventId::RadiobuttonToggle, [this] () { maToggleHdl.Call(*this); } );
}
 
void RadioButton::SetModeRadioImage( const Image& rImage )
{
    if ( rImage != maImage )
    {
        maImage = rImage;
        CompatStateChanged( StateChangedType::Data );
        queue_resize();
    }
}
 
 
void RadioButton::SetState( bool bCheck )
{
    // carry the TabStop flag along correctly
    if ( bCheck )
        mpWindowImpl->mnStyle |= WB_TABSTOP;
    else
        mpWindowImpl->mnStyle &= ~WB_TABSTOP;
 
    if ( mbChecked != bCheck )
    {
        mbChecked = bCheck;
        CompatStateChanged( StateChangedType::State );
        Toggle();
    }
}
 
bool RadioButton::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "active")
        SetState(toBool(rValue));
    else if (rKey == "image-position")
    {
        WinBits nBits = GetStyle();
        if (rValue == "left")
        {
            nBits &= ~(WB_CENTER | WB_RIGHT);
            nBits |= WB_LEFT;
        }
        else if (rValue == "right")
        {
            nBits &= ~(WB_CENTER | WB_LEFT);
            nBits |= WB_RIGHT;
        }
        else if (rValue == "top")
        {
            nBits &= ~(WB_VCENTER | WB_BOTTOM);
            nBits |= WB_TOP;
        }
        else if (rValue == "bottom")
        {
            nBits &= ~(WB_VCENTER | WB_TOP);
            nBits |= WB_BOTTOM;
        }
        //It's rather mad to have to set these bits when there is the other
        //image align. Looks like e.g. the radiobuttons etc weren't converted
        //over to image align fully.
        SetStyle(nBits);
        //Deliberate to set the sane ImageAlign property
        return Button::set_property(rKey, rValue);
    }
    else
        return Button::set_property(rKey, rValue);
    return true;
}
 
void RadioButton::Check( bool bCheck )
{
    // TabStop-Flag richtig mitfuehren
    if ( bCheck )
        mpWindowImpl->mnStyle |= WB_TABSTOP;
    else
        mpWindowImpl->mnStyle &= ~WB_TABSTOP;
 
    if ( mbChecked == bCheck )
        return;
 
    mbChecked = bCheck;
    VclPtr<vcl::Window> xWindow = this;
    CompatStateChanged( StateChangedType::State );
    if ( xWindow->isDisposed() )
        return;
    if ( bCheck && mbRadioCheck )
        ImplUncheckAllOther();
    if ( xWindow->isDisposed() )
        return;
    Toggle();
}
 
tools::Long Button::ImplGetImageToTextDistance() const
{
    // 4 pixels, but take zoom into account, so the text doesn't "jump" relative to surrounding elements,
    // which might have been aligned with the text of the check box
    return CalcZoom( 4 );
}
 
Size RadioButton::ImplGetRadioImageSize() const
{
    Size aSize;
    bool bDefaultSize = true;
    if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
    {
        ImplControlValue aControlValue;
        tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
        tools::Rectangle aBoundingRgn, aContentRgn;
 
        // get native size of a radio button
        if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
                                    ControlState::DEFAULT|ControlState::ENABLED,
                                    aControlValue,
                                    aBoundingRgn, aContentRgn ) )
        {
            aSize = aContentRgn.GetSize();
            bDefaultSize = false;
        }
    }
    if( bDefaultSize )
        aSize = GetRadioImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
    return aSize;
}
 
static void LoadThemedImageList(const StyleSettings &rStyleSettings,
                                std::vector<Image>& rList, const std::vector<OUString> &rResources)
{
    Color aColorAry1[6];
    Color aColorAry2[6];
    aColorAry1[0] = Color( 0xC0, 0xC0, 0xC0 );
    aColorAry1[1] = Color( 0xFF, 0xFF, 0x00 );
    aColorAry1[2] = Color( 0xFF, 0xFF, 0xFF );
    aColorAry1[3] = Color( 0x80, 0x80, 0x80 );
    aColorAry1[4] = Color( 0x00, 0x00, 0x00 );
    aColorAry1[5] = Color( 0x00, 0xFF, 0x00 );
    aColorAry2[0] = rStyleSettings.GetFaceColor();
    aColorAry2[1] = rStyleSettings.GetWindowColor();
    aColorAry2[2] = rStyleSettings.GetLightColor();
    aColorAry2[3] = rStyleSettings.GetShadowColor();
    aColorAry2[4] = rStyleSettings.GetDarkShadowColor();
    aColorAry2[5] = rStyleSettings.GetWindowTextColor();
 
    static_assert( sizeof(aColorAry1) == sizeof(aColorAry2), "aColorAry1 must match aColorAry2" );
 
    for (const auto &a : rResources)
    {
        BitmapEx aBmpEx(a);
        aBmpEx.Replace(aColorAry1, aColorAry2, SAL_N_ELEMENTS(aColorAry1));
        rList.emplace_back(aBmpEx);
    }
}
 
Image RadioButton::GetRadioImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
{
    ImplSVData*             pSVData = ImplGetSVData();
    const StyleSettings&    rStyleSettings = rSettings.GetStyleSettings();
    sal_uInt16              nStyle = 0;
 
    if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
        nStyle = STYLE_RADIOBUTTON_MONO;
 
    if ( pSVData->maCtrlData.maRadioImgList.empty() ||
         (pSVData->maCtrlData.mnRadioStyle != nStyle) ||
         (pSVData->maCtrlData.mnLastRadioFColor != rStyleSettings.GetFaceColor()) ||
         (pSVData->maCtrlData.mnLastRadioWColor != rStyleSettings.GetWindowColor()) ||
         (pSVData->maCtrlData.mnLastRadioLColor != rStyleSettings.GetLightColor()) )
    {
        pSVData->maCtrlData.maRadioImgList.clear();
 
        pSVData->maCtrlData.mnLastRadioFColor = rStyleSettings.GetFaceColor();
        pSVData->maCtrlData.mnLastRadioWColor = rStyleSettings.GetWindowColor();
        pSVData->maCtrlData.mnLastRadioLColor = rStyleSettings.GetLightColor();
 
        std::vector<OUString> aResources;
        if (nStyle)
        {
            aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO1);
            aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO2);
            aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO3);
            aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO4);
            aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO5);
            aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO6);
        }
        else
        {
            aResources.emplace_back(SV_RESID_BITMAP_RADIO1);
            aResources.emplace_back(SV_RESID_BITMAP_RADIO2);
            aResources.emplace_back(SV_RESID_BITMAP_RADIO3);
            aResources.emplace_back(SV_RESID_BITMAP_RADIO4);
            aResources.emplace_back(SV_RESID_BITMAP_RADIO5);
            aResources.emplace_back(SV_RESID_BITMAP_RADIO6);
        }
        LoadThemedImageList( rStyleSettings, pSVData->maCtrlData.maRadioImgList, aResources);
        pSVData->maCtrlData.mnRadioStyle = nStyle;
    }
 
    sal_uInt16 nIndex;
    if ( nFlags & DrawButtonFlags::Disabled )
    {
        if ( nFlags & DrawButtonFlags::Checked )
            nIndex = 5;
        else
            nIndex = 4;
    }
    else if ( nFlags & DrawButtonFlags::Pressed )
    {
        if ( nFlags & DrawButtonFlags::Checked )
            nIndex = 3;
        else
            nIndex = 2;
    }
    else
    {
        if ( nFlags & DrawButtonFlags::Checked )
            nIndex = 1;
        else
            nIndex = 0;
    }
    return pSVData->maCtrlData.maRadioImgList[nIndex];
}
 
void RadioButton::ImplAdjustNWFSizes()
{
    GetOutDev()->Push( vcl::PushFlags::MAPMODE );
    SetMapMode(MapMode(MapUnit::MapPixel));
 
    ImplControlValue aControlValue;
    Size aCurSize( GetSizePixel() );
    tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
    tools::Rectangle aBoundingRgn, aContentRgn;
 
    // get native size of a radiobutton
    if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
                                ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
                                aBoundingRgn, aContentRgn ) )
    {
        Size aSize = aContentRgn.GetSize();
 
        if( aSize.Height() > aCurSize.Height() )
        {
            aCurSize.setHeight( aSize.Height() );
            SetSizePixel( aCurSize );
        }
    }
 
    GetOutDev()->Pop();
}
 
Size RadioButton::CalcMinimumSize(tools::Long nMaxWidth) const
{
    Size aSize;
    if ( !maImage )
        aSize = ImplGetRadioImageSize();
    else
    {
        aSize = maImage.GetSizePixel();
        aSize.AdjustWidth(8);
        aSize.AdjustHeight(8);
    }
 
    if (Button::HasImage())
    {
        Size aImgSize = GetModeImage().GetSizePixel();
        aSize = Size(std::max(aImgSize.Width(), aSize.Width()),
                     std::max(aImgSize.Height(), aSize.Height()));
    }
 
    OUString aText = GetText();
    if (!aText.isEmpty())
    {
        bool bTopImage = (GetStyle() & WB_TOP) != 0;
 
        Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
                                      aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
 
        aSize.AdjustWidth(2 );   // for focus rect
 
        if (!bTopImage)
        {
            aSize.AdjustWidth(ImplGetImageToTextDistance() );
            aSize.AdjustWidth(aTextSize.Width() );
            if ( aSize.Height() < aTextSize.Height() )
                aSize.setHeight( aTextSize.Height() );
        }
        else
        {
            aSize.AdjustHeight(6 );
            aSize.AdjustHeight(GetTextHeight() );
            if ( aSize.Width() < aTextSize.Width() )
                aSize.setWidth( aTextSize.Width() );
        }
    }
 
    return CalcWindowSize( aSize );
}
 
Size RadioButton::GetOptimalSize() const
{
    return CalcMinimumSize();
}
 
void RadioButton::ShowFocus(const tools::Rectangle& rRect)
{
    if (IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Focus))
    {
        ImplControlValue aControlValue;
        tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
 
        aInRect.SetLeft( rRect.Left() );  // exclude the radio element itself from the focusrect
 
        GetOutDev()->DrawNativeControl(ControlType::Radiobutton, ControlPart::Focus, aInRect,
                          ControlState::FOCUSED, aControlValue, OUString());
    }
    Button::ShowFocus(rRect);
}
 
void RadioButton::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    Button::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("checked", IsChecked());
 
    OUString sGroupId;
    std::vector<VclPtr<RadioButton>> aGroup = GetRadioButtonGroup();
    for(const auto& pButton : aGroup)
        sGroupId += pButton->get_id();
 
    if (!sGroupId.isEmpty())
        rJsonWriter.put("group", sGroupId);
 
    if (!!maImage)
    {
        SvMemoryStream aOStm(6535, 6535);
        if(GraphicConverter::Export(aOStm, maImage.GetBitmapEx(), ConvertDataFormat::PNG) == ERRCODE_NONE)
        {
            css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
            OStringBuffer aBuffer("data:image/png;base64,");
            ::comphelper::Base64::encode(aBuffer, aSeq);
            rJsonWriter.put("image", aBuffer);
        }
    }
}
 
FactoryFunction RadioButton::GetUITestFactory() const
{
    return RadioButtonUIObject::create;
}
 
void CheckBox::ImplInitCheckBoxData()
{
    meState         = TRISTATE_FALSE;
    mbTriState      = false;
}
 
void CheckBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
{
    nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
    Button::ImplInit( pParent, nStyle, nullptr );
 
    ImplInitSettings( true );
}
 
WinBits CheckBox::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
{
    if ( !(nStyle & WB_NOTABSTOP) )
        nStyle |= WB_TABSTOP;
    if ( !(nStyle & WB_NOGROUP) &&
         (!pPrevWindow || (pPrevWindow->GetType() != WindowType::CHECKBOX)) )
        nStyle |= WB_GROUP;
    return nStyle;
}
 
const vcl::Font& CheckBox::GetCanonicalFont( const StyleSettings& _rStyle ) const
{
    return _rStyle.GetRadioCheckFont();
}
 
const Color& CheckBox::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
{
    return _rStyle.GetRadioCheckTextColor();
}
 
void CheckBox::ImplInitSettings( bool bBackground )
{
    Button::ImplInitSettings();
 
    if ( !bBackground )
        return;
 
    vcl::Window* pParent = GetParent();
    if ( !IsControlBackground() &&
        (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) ) )
    {
        EnableChildTransparentMode();
        SetParentClipMode( ParentClipMode::NoClip );
        SetPaintTransparent( true );
        SetBackground();
        if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
            ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
    }
    else
    {
        EnableChildTransparentMode( false );
        SetParentClipMode();
        SetPaintTransparent( false );
 
        if ( IsControlBackground() )
            SetBackground( GetControlBackground() );
        else
            SetBackground( pParent->GetBackground() );
    }
}
 
void CheckBox::ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext)
{
    bool bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire);
    if (bNativeOK)
    {
        ImplControlValue aControlValue(meState == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
        tools::Rectangle aCtrlRegion(maStateRect);
        ControlState nState = ControlState::NONE;
 
        if (HasFocus())
            nState |= ControlState::FOCUSED;
        if (GetButtonState() & DrawButtonFlags::Default)
            nState |= ControlState::DEFAULT;
        if (GetButtonState() & DrawButtonFlags::Pressed)
            nState |= ControlState::PRESSED;
        if (IsEnabled())
            nState |= ControlState::ENABLED;
 
        if (meState == TRISTATE_TRUE)
            aControlValue.setTristateVal(ButtonValue::On);
        else if (meState == TRISTATE_INDET)
            aControlValue.setTristateVal(ButtonValue::Mixed);
 
        if (IsMouseOver() && maMouseRect.Contains(GetPointerPosPixel()))
            nState |= ControlState::ROLLOVER;
 
        bNativeOK = rRenderContext.DrawNativeControl(ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
                                                     nState, aControlValue, OUString());
    }
 
    if (bNativeOK)
        return;
 
    DrawButtonFlags nStyle = GetButtonState();
    if (!IsEnabled())
        nStyle |= DrawButtonFlags::Disabled;
    if (meState == TRISTATE_INDET)
        nStyle |= DrawButtonFlags::DontKnow;
    else if (meState == TRISTATE_TRUE)
        nStyle |= DrawButtonFlags::Checked;
    Image aImage = GetCheckImage(GetSettings(), nStyle);
    if (IsZoom())
        rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
    else
        rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
}
 
void CheckBox::ImplDraw( OutputDevice* pDev, SystemTextColorFlags nSystemTextColorFlags,
                         const Point& rPos, const Size& rSize,
                         const Size& rImageSize, tools::Rectangle& rStateRect,
                         tools::Rectangle& rMouseRect )
{
    WinBits                 nWinStyle = GetStyle();
    OUString                aText( GetText() );
 
    pDev->Push( vcl::PushFlags::CLIPREGION | vcl::PushFlags::LINECOLOR );
    pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
 
    if (!aText.isEmpty() || HasImage())
    {
        Button::ImplDrawRadioCheck(pDev, nWinStyle, nSystemTextColorFlags,
                                   rPos, rSize, rImageSize,
                                   rStateRect, rMouseRect);
    }
    else
    {
        rStateRect.SetLeft( rPos.X() );
        if ( nWinStyle & WB_VCENTER )
            rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
        else if ( nWinStyle & WB_BOTTOM )
            rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() );
        else
            rStateRect.SetTop( rPos.Y() );
        rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
        rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
        // provide space for focusrect
        // note: this assumes that the control's size was adjusted
        // accordingly in Get/LoseFocus, so the onscreen position won't change
        if( HasFocus() )
            rStateRect.Move( 1, 1 );
        rMouseRect          = rStateRect;
 
        ImplSetFocusRect( rStateRect );
    }
 
    pDev->Pop();
}
 
void CheckBox::ImplDrawCheckBox(vcl::RenderContext& rRenderContext)
{
    Size aImageSize = ImplGetCheckImageSize();
    aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
    aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
 
    HideFocus();
 
    ImplDraw(&rRenderContext, SystemTextColorFlags::NONE, Point(), GetOutputSizePixel(),
             aImageSize, maStateRect, maMouseRect);
 
    ImplDrawCheckBoxState(rRenderContext);
    if (HasFocus())
        ShowFocus(ImplGetFocusRect());
}
 
void CheckBox::ImplCheck()
{
    TriState eNewState;
    if ( meState == TRISTATE_FALSE )
        eNewState = TRISTATE_TRUE;
    else if ( !mbTriState )
        eNewState = TRISTATE_FALSE;
    else if ( meState == TRISTATE_TRUE )
        eNewState = TRISTATE_INDET;
    else
        eNewState = TRISTATE_FALSE;
    meState = eNewState;
 
    VclPtr<vcl::Window> xWindow = this;
    Invalidate();
    Toggle();
    if ( xWindow->isDisposed() )
        return;
    Click();
}
 
CheckBox::CheckBox( vcl::Window* pParent, WinBits nStyle ) :
    Button( WindowType::CHECKBOX )
{
    ImplInitCheckBoxData();
    ImplInit( pParent, nStyle );
}
 
void CheckBox::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( rMEvt.IsLeft() && maMouseRect.Contains( rMEvt.GetPosPixel() ) )
    {
        GetButtonState() |= DrawButtonFlags::Pressed;
        Invalidate();
        StartTracking();
        return;
    }
 
    Button::MouseButtonDown( rMEvt );
}
 
void CheckBox::Tracking( const TrackingEvent& rTEvt )
{
    if ( rTEvt.IsTrackingEnded() )
    {
        if ( GetButtonState() & DrawButtonFlags::Pressed )
        {
            if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
                GrabFocus();
 
            GetButtonState() &= ~DrawButtonFlags::Pressed;
 
            // do not call click handler if aborted
            if ( !rTEvt.IsTrackingCanceled() )
                ImplCheck();
            else
            {
                Invalidate();
            }
        }
    }
    else
    {
        if ( maMouseRect.Contains( rTEvt.GetMouseEvent().GetPosPixel() ) )
        {
            if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
            {
                GetButtonState() |= DrawButtonFlags::Pressed;
                Invalidate();
            }
        }
        else
        {
            if ( GetButtonState() & DrawButtonFlags::Pressed )
            {
                GetButtonState() &= ~DrawButtonFlags::Pressed;
                Invalidate();
            }
        }
    }
}
 
void CheckBox::KeyInput( const KeyEvent& rKEvt )
{
    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
 
    if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
    {
        if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
        {
            GetButtonState() |= DrawButtonFlags::Pressed;
            Invalidate();
        }
    }
    else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
    {
        GetButtonState() &= ~DrawButtonFlags::Pressed;
        Invalidate();
    }
    else
        Button::KeyInput( rKEvt );
}
 
void CheckBox::KeyUp( const KeyEvent& rKEvt )
{
    vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
 
    if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
    {
        GetButtonState() &= ~DrawButtonFlags::Pressed;
        ImplCheck();
    }
    else
        Button::KeyUp( rKEvt );
}
 
void CheckBox::FillLayoutData() const
{
    mxLayoutData.emplace();
    const_cast<CheckBox*>(this)->Invalidate();
}
 
void CheckBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
{
    ImplDrawCheckBox(rRenderContext);
}
 
void CheckBox::Draw( OutputDevice* pDev, const Point& rPos,
                     SystemTextColorFlags nFlags )
{
    MapMode     aResMapMode( MapUnit::Map100thMM );
    Point       aPos  = pDev->LogicToPixel( rPos );
    Size        aSize = GetSizePixel();
    Size        aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
    Size        aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
    Size        aBrd2Size = pDev->LogicToPixel( Size( 30, 30 ), aResMapMode );
    tools::Long        nCheckWidth = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode ).Width();
    vcl::Font   aFont = GetDrawPixelFont( pDev );
    tools::Rectangle   aStateRect;
    tools::Rectangle   aMouseRect;
 
    aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
    aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
    aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
    aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
    aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
    aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
 
    if ( !aBrd1Size.Width() )
        aBrd1Size.setWidth( 1 );
    if ( !aBrd1Size.Height() )
        aBrd1Size.setHeight( 1 );
    if ( !aBrd2Size.Width() )
        aBrd2Size.setWidth( 1 );
    if ( !aBrd2Size.Height() )
        aBrd2Size.setHeight( 1 );
    if ( !nCheckWidth )
        nCheckWidth = 1;
 
    pDev->Push();
    pDev->SetMapMode();
    pDev->SetFont( aFont );
    if ( nFlags & SystemTextColorFlags::Mono )
        pDev->SetTextColor( COL_BLACK );
    else
        pDev->SetTextColor( GetTextColor() );
    pDev->SetTextFillColor();
 
    ImplDraw( pDev, nFlags, aPos, aSize,
              aImageSize, aStateRect, aMouseRect );
 
    pDev->SetLineColor();
    pDev->SetFillColor( COL_BLACK );
    pDev->DrawRect( aStateRect );
    aStateRect.AdjustLeft(aBrd1Size.Width() );
    aStateRect.AdjustTop(aBrd1Size.Height() );
    aStateRect.AdjustRight( -(aBrd1Size.Width()) );
    aStateRect.AdjustBottom( -(aBrd1Size.Height()) );
    if ( meState == TRISTATE_INDET )
        pDev->SetFillColor( COL_LIGHTGRAY );
    else
        pDev->SetFillColor( COL_WHITE );
    pDev->DrawRect( aStateRect );
 
    if ( meState == TRISTATE_TRUE )
    {
        aStateRect.AdjustLeft(aBrd2Size.Width() );
        aStateRect.AdjustTop(aBrd2Size.Height() );
        aStateRect.AdjustRight( -(aBrd2Size.Width()) );
        aStateRect.AdjustBottom( -(aBrd2Size.Height()) );
        Point   aPos11( aStateRect.TopLeft() );
        Point   aPos12( aStateRect.BottomRight() );
        Point   aPos21( aStateRect.TopRight() );
        Point   aPos22( aStateRect.BottomLeft() );
        Point   aTempPos11( aPos11 );
        Point   aTempPos12( aPos12 );
        Point   aTempPos21( aPos21 );
        Point   aTempPos22( aPos22 );
        pDev->SetLineColor( COL_BLACK );
        tools::Long nDX = 0;
        for ( tools::Long i = 0; i < nCheckWidth; i++ )
        {
            if ( !(i % 2) )
            {
                aTempPos11.setX( aPos11.X()+nDX );
                aTempPos12.setX( aPos12.X()+nDX );
                aTempPos21.setX( aPos21.X()+nDX );
                aTempPos22.setX( aPos22.X()+nDX );
            }
            else
            {
                nDX++;
                aTempPos11.setX( aPos11.X()-nDX );
                aTempPos12.setX( aPos12.X()-nDX );
                aTempPos21.setX( aPos21.X()-nDX );
                aTempPos22.setX( aPos22.X()-nDX );
            }
            pDev->DrawLine( aTempPos11, aTempPos12 );
            pDev->DrawLine( aTempPos21, aTempPos22 );
        }
    }
 
    pDev->Pop();
}
 
void CheckBox::Resize()
{
    Control::Resize();
    Invalidate();
}
 
void CheckBox::GetFocus()
{
    if (GetText().isEmpty())
    {
        // increase button size to have space for focus rect
        // checkboxes without text will draw focusrect around the check
        // See CheckBox::ImplDraw()
        Point aPos( GetPosPixel() );
        Size aSize( GetSizePixel() );
        aPos.Move(-1,-1);
        aSize.AdjustHeight(2 );
        aSize.AdjustWidth(2 );
        setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
        Invalidate();
        // Trigger drawing to initialize the mouse rectangle, otherwise the mouse button down
        // handler would ignore the mouse event.
        PaintImmediately();
    }
    else
        ShowFocus( ImplGetFocusRect() );
 
    SetInputContext( InputContext( GetFont() ) );
    Button::GetFocus();
}
 
void CheckBox::LoseFocus()
{
    if ( GetButtonState() & DrawButtonFlags::Pressed )
    {
        GetButtonState() &= ~DrawButtonFlags::Pressed;
        Invalidate();
    }
 
    HideFocus();
    Button::LoseFocus();
 
    if (GetText().isEmpty())
    {
        // decrease button size again (see GetFocus())
        // checkboxes without text will draw focusrect around the check
        Point aPos( GetPosPixel() );
        Size aSize( GetSizePixel() );
        aPos.Move(1,1);
        aSize.AdjustHeight( -2 );
        aSize.AdjustWidth( -2 );
        setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
        Invalidate();
    }
}
 
void CheckBox::StateChanged( StateChangedType nType )
{
    Button::StateChanged( nType );
 
    if ( nType == StateChangedType::State )
    {
        if ( IsReallyVisible() && IsUpdateMode() )
            Invalidate( maStateRect );
    }
    else if ( (nType == StateChangedType::Enable) ||
              (nType == StateChangedType::Text) ||
              (nType == StateChangedType::Data) ||
              (nType == StateChangedType::UpdateMode) )
    {
        if ( IsUpdateMode() )
            Invalidate();
    }
    else if ( nType == StateChangedType::Style )
    {
        SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
 
        if ( (GetPrevStyle() & CHECKBOX_VIEW_STYLE) !=
             (GetStyle() & CHECKBOX_VIEW_STYLE) )
        {
            if ( IsUpdateMode() )
                Invalidate();
        }
    }
    else if ( (nType == StateChangedType::Zoom) ||
              (nType == StateChangedType::ControlFont) )
    {
        ImplInitSettings( false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlForeground )
    {
        ImplInitSettings( false );
        Invalidate();
    }
    else if ( nType == StateChangedType::ControlBackground )
    {
        ImplInitSettings( true );
        Invalidate();
    }
}
 
void CheckBox::DataChanged( const DataChangedEvent& rDCEvt )
{
    Button::DataChanged( rDCEvt );
 
    if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
         (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
         ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
    {
        ImplInitSettings( true );
        Invalidate();
    }
}
 
bool CheckBox::PreNotify( NotifyEvent& rNEvt )
{
    if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
    {
        const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
        if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
        {
            // trigger redraw if mouse over state has changed
            if( IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire) )
            {
                if (maMouseRect.Contains(GetPointerPosPixel()) != maMouseRect.Contains(GetLastPointerPosPixel()) ||
                    pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
                {
                    Invalidate( maStateRect );
                }
            }
        }
    }
 
    return Button::PreNotify(rNEvt);
}
 
void CheckBox::Toggle()
{
    ImplCallEventListenersAndHandler( VclEventId::CheckboxToggle, [this] () { maToggleHdl.Call(*this); } );
}
 
void CheckBox::SetState( TriState eState )
{
    if ( !mbTriState && (eState == TRISTATE_INDET) )
        eState = TRISTATE_FALSE;
 
    if ( meState != eState )
    {
        meState = eState;
        StateChanged( StateChangedType::State );
        Toggle();
    }
}
 
bool CheckBox::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "active")
        SetState(toBool(rValue) ? TRISTATE_TRUE : TRISTATE_FALSE);
    else
        return Button::set_property(rKey, rValue);
    return true;
}
 
void CheckBox::EnableTriState( bool bTriState )
{
    if ( mbTriState != bTriState )
    {
        mbTriState = bTriState;
 
        if ( !bTriState && (meState == TRISTATE_INDET) )
            SetState( TRISTATE_FALSE );
    }
}
 
Size CheckBox::ImplGetCheckImageSize() const
{
    Size aSize;
    bool bDefaultSize = true;
    if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
    {
        ImplControlValue aControlValue;
        tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
        tools::Rectangle aBoundingRgn, aContentRgn;
 
        // get native size of a check box
        if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
                                    ControlState::DEFAULT|ControlState::ENABLED,
                                    aControlValue,
                                    aBoundingRgn, aContentRgn ) )
        {
            aSize = aContentRgn.GetSize();
            bDefaultSize = false;
        }
    }
    if( bDefaultSize )
        aSize = GetCheckImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
    return aSize;
}
 
Image CheckBox::GetCheckImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
{
    ImplSVData*             pSVData = ImplGetSVData();
    const StyleSettings&    rStyleSettings = rSettings.GetStyleSettings();
    sal_uInt16              nStyle = 0;
 
    if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
        nStyle = STYLE_CHECKBOX_MONO;
 
    if ( pSVData->maCtrlData.maCheckImgList.empty() ||
         (pSVData->maCtrlData.mnCheckStyle != nStyle) ||
         (pSVData->maCtrlData.mnLastCheckFColor != rStyleSettings.GetFaceColor()) ||
         (pSVData->maCtrlData.mnLastCheckWColor != rStyleSettings.GetWindowColor()) ||
         (pSVData->maCtrlData.mnLastCheckLColor != rStyleSettings.GetLightColor()) )
    {
        pSVData->maCtrlData.maCheckImgList.clear();
 
        pSVData->maCtrlData.mnLastCheckFColor = rStyleSettings.GetFaceColor();
        pSVData->maCtrlData.mnLastCheckWColor = rStyleSettings.GetWindowColor();
        pSVData->maCtrlData.mnLastCheckLColor = rStyleSettings.GetLightColor();
 
        std::vector<OUString> aResources;
        if (nStyle)
        {
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO1);
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO2);
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO3);
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO4);
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO5);
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO6);
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO7);
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO8);
            aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO9);
        }
        else
        {
            aResources.emplace_back(SV_RESID_BITMAP_CHECK1);
            aResources.emplace_back(SV_RESID_BITMAP_CHECK2);
            aResources.emplace_back(SV_RESID_BITMAP_CHECK3);
            aResources.emplace_back(SV_RESID_BITMAP_CHECK4);
            aResources.emplace_back(SV_RESID_BITMAP_CHECK5);
            aResources.emplace_back(SV_RESID_BITMAP_CHECK6);
            aResources.emplace_back(SV_RESID_BITMAP_CHECK7);
            aResources.emplace_back(SV_RESID_BITMAP_CHECK8);
            aResources.emplace_back(SV_RESID_BITMAP_CHECK9);
        }
        LoadThemedImageList(rStyleSettings, pSVData->maCtrlData.maCheckImgList, aResources);
        pSVData->maCtrlData.mnCheckStyle = nStyle;
    }
 
    sal_uInt16 nIndex;
    if ( nFlags & DrawButtonFlags::Disabled )
    {
        if ( nFlags & DrawButtonFlags::DontKnow )
            nIndex = 8;
        else if ( nFlags & DrawButtonFlags::Checked )
            nIndex = 5;
        else
            nIndex = 4;
    }
    else if ( nFlags & DrawButtonFlags::Pressed )
    {
        if ( nFlags & DrawButtonFlags::DontKnow )
            nIndex = 7;
        else if ( nFlags & DrawButtonFlags::Checked )
            nIndex = 3;
        else
            nIndex = 2;
    }
    else
    {
        if ( nFlags & DrawButtonFlags::DontKnow )
            nIndex = 6;
        else if ( nFlags & DrawButtonFlags::Checked )
            nIndex = 1;
        else
            nIndex = 0;
    }
    return pSVData->maCtrlData.maCheckImgList[nIndex];
}
 
void CheckBox::ImplAdjustNWFSizes()
{
    GetOutDev()->Push( vcl::PushFlags::MAPMODE );
    SetMapMode(MapMode(MapUnit::MapPixel));
 
    ImplControlValue aControlValue;
    Size aCurSize( GetSizePixel() );
    tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
    tools::Rectangle aBoundingRgn, aContentRgn;
 
    // get native size of a radiobutton
    if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
                                ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
                                aBoundingRgn, aContentRgn ) )
    {
        Size aSize = aContentRgn.GetSize();
 
        if( aSize.Height() > aCurSize.Height() )
        {
            aCurSize.setHeight( aSize.Height() );
            SetSizePixel( aCurSize );
        }
    }
 
    GetOutDev()->Pop();
}
 
Size CheckBox::CalcMinimumSize( tools::Long nMaxWidth ) const
{
    Size aSize = ImplGetCheckImageSize();
    nMaxWidth -= aSize.Width();
 
    OUString aText = GetText();
    if (!aText.isEmpty())
    {
        // subtract what will be added later
        nMaxWidth-=2;
        nMaxWidth -= ImplGetImageToTextDistance();
 
        Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
                                      aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
        aSize.AdjustWidth(2 );    // for focus rect
        aSize.AdjustWidth(ImplGetImageToTextDistance() );
        aSize.AdjustWidth(aTextSize.Width() );
        if ( aSize.Height() < aTextSize.Height() )
            aSize.setHeight( aTextSize.Height() );
    }
    else
    {
        // is this still correct ? since the checkbox now
        // shows a focus rect it should be 2 pixels wider and longer
/* since otherwise the controls in the Writer hang too far up
        aSize.Width() += 2;
        aSize.Height() += 2;
*/
    }
 
    return CalcWindowSize( aSize );
}
 
Size CheckBox::GetOptimalSize() const
{
    int nWidthRequest(get_width_request());
    return CalcMinimumSize(nWidthRequest != -1 ? nWidthRequest : 0);
}
 
void CheckBox::ShowFocus(const tools::Rectangle& rRect)
{
    if (IsNativeControlSupported(ControlType::Checkbox, ControlPart::Focus))
    {
        ImplControlValue aControlValue;
        tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
 
        aInRect.SetLeft( rRect.Left() );  // exclude the checkbox itself from the focusrect
 
        GetOutDev()->DrawNativeControl(ControlType::Checkbox, ControlPart::Focus, aInRect,
                          ControlState::FOCUSED, aControlValue, OUString());
    }
    Button::ShowFocus(rRect);
}
 
void CheckBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    Button::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("checked", IsChecked());
}
 
FactoryFunction CheckBox::GetUITestFactory() const
{
    return CheckBoxUIObject::create;
}
 
ImageButton::ImageButton( vcl::Window* pParent, WinBits nStyle ) :
    PushButton( pParent, nStyle )
{
    ImplInitStyle();
}
 
void ImageButton::ImplInitStyle()
{
    WinBits nStyle = GetStyle();
 
    if ( ! ( nStyle & ( WB_RIGHT | WB_LEFT ) ) )
        nStyle |= WB_CENTER;
 
    if ( ! ( nStyle & ( WB_TOP | WB_BOTTOM ) ) )
        nStyle |= WB_VCENTER;
 
    SetStyle( nStyle );
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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

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

V719 The switch statement does not cover all values of the 'SymbolType' enum: MENU.

V1048 The 'bNativeOK' variable was assigned the same value.