summaryrefslogtreecommitdiffstats
path: root/vcl/source/control/button.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/source/control/button.cxx3826
1 files changed, 3826 insertions, 0 deletions
diff --git a/vcl/source/control/button.cxx b/vcl/source/control/button.cxx
new file mode 100644
index 000000000..210d2b629
--- /dev/null
+++ b/vcl/source/control/button.cxx
@@ -0,0 +1,3826 @@
+/* -*- 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)
+{
+ maCommand = aCommand;
+ SetClickHdl( LINK( this, Button, dispatchCommandHandler) );
+
+ mpButtonData->mpStatusListener = new VclStatusListener<Button>(this, 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.getWidth() > 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
+{
+
+const char* 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());
+ OUStringBuffer aBuffer("data:image/png;base64,");
+ ::comphelper::Base64::encode(aBuffer, aSeq);
+ rJsonWriter.put("image", aBuffer.makeStringAndClear());
+ }
+ }
+
+ 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();
+
+ 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;
+
+ // 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;
+ if (GetStyle() & WB_BEVELBUTTON)
+ aControlValue.mbBevelButton = 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 ((nState & ControlState::ROLLOVER) || !(GetStyle() & WB_FLATBUTTON)
+ || (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);
+ if (bRollOver)
+ 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() == MouseNotifyEvent::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() == MouseNotifyEvent::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());
+ OUStringBuffer aBuffer("data:image/png;base64,");
+ ::comphelper::Base64::encode(aBuffer, aSeq);
+ rJsonWriter.put("image", aBuffer.makeStringAndClear());
+ }
+ }
+}
+
+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() == MouseNotifyEvent::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: */