/* -*- 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.