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

Quelle  button.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <tools/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 <accessibility/vclxaccessiblebutton.hxx>
#include <accessibility/vclxaccessiblecheckbox.hxx>
#include <accessibility/vclxaccessibleradiobutton.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 eType ) :
    Control( eType ),
    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 &&nbsp;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 );
}

css::uno::Reference<css::accessibility::XAccessible> PushButton::CreateAccessible()
{
    return new VCLXAccessibleButton(this);
}

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() )
            {
                Dialog* pDialog = static_cast<Dialog*>(pParent);
                if (pDialog->IsInExecute())
                    pDialog->EndDialog();
                // prevent recursive calls
                else if (!pDialog->IsInClose())
                {
                    if ( pParent->GetStyle() & WB_CLOSEABLE )
                        pDialog->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();
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.