Files
loongoffice/vcl/source/control/button.cxx
Caolán McNamara 102e15d6f3 Related: tdf#152486 infobar is white text on light button in light mode
background isn't "bright" or "dark" enough to be considered one or the
other. This is only used for the infobars, so black text is good enough
for the current bg colors in use.

Change-Id: I4c2c4fadac72cc35aebeadec417186c6358c0a77
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146667
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
2023-02-08 19:26:57 +00:00

3844 lines
122 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <tools/poly.hxx>
#include <vcl/builder.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/image.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/decoview.hxx>
#include <vcl/event.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/toolkit/dialog.hxx>
#include <vcl/toolkit/fixed.hxx>
#include <vcl/toolkit/button.hxx>
#include <vcl/salnativewidgets.hxx>
#include <vcl/toolkit/edit.hxx>
#include <vcl/layout.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/uitest/uiobject.hxx>
#include <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("");
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 OString &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()));
}
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 OString &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("ok");
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("cancel");
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("help");
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)
{
m_xGroup->erase(std::remove(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(this)),
m_xGroup->end());
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 OString &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 OString &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: */