loplugin:unusedfields
[LibreOffice.git] / vcl / source / control / button.cxx
blob4c18207e084420bfc203129d280f7f26c5564cf6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/poly.hxx>
22 #include <vcl/image.hxx>
23 #include <vcl/bitmapex.hxx>
24 #include <vcl/decoview.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/dialog.hxx>
29 #include <vcl/fixed.hxx>
30 #include <vcl/button.hxx>
31 #include <vcl/salnativewidgets.hxx>
32 #include <vcl/edit.hxx>
33 #include <vcl/layout.hxx>
34 #include <vcl/vclstatuslistener.hxx>
35 #include <vcl/uitest/uiobject.hxx>
37 #include <strings.hrc>
38 #include <bitmaps.hlst>
39 #include <svdata.hxx>
40 #include <window.h>
41 #include <controldata.hxx>
42 #include <osl/diagnose.h>
44 #include <comphelper/dispatchcommand.hxx>
45 #include <comphelper/lok.hxx>
46 #include <officecfg/Office/Common.hxx>
49 using namespace css;
51 static constexpr auto PUSHBUTTON_VIEW_STYLE = WB_3DLOOK |
52 WB_LEFT | WB_CENTER | WB_RIGHT |
53 WB_TOP | WB_VCENTER | WB_BOTTOM |
54 WB_WORDBREAK | WB_NOLABEL |
55 WB_DEFBUTTON | WB_NOLIGHTBORDER |
56 WB_RECTSTYLE | WB_SMALLSTYLE |
57 WB_TOGGLE;
58 static constexpr auto RADIOBUTTON_VIEW_STYLE = WB_3DLOOK |
59 WB_LEFT | WB_CENTER | WB_RIGHT |
60 WB_TOP | WB_VCENTER | WB_BOTTOM |
61 WB_WORDBREAK | WB_NOLABEL;
62 static constexpr auto CHECKBOX_VIEW_STYLE = WB_3DLOOK |
63 WB_LEFT | WB_CENTER | WB_RIGHT |
64 WB_TOP | WB_VCENTER | WB_BOTTOM |
65 WB_WORDBREAK | WB_NOLABEL;
67 #define STYLE_RADIOBUTTON_MONO (sal_uInt16(0x0001)) // legacy
68 #define STYLE_CHECKBOX_MONO (sal_uInt16(0x0001)) // legacy
70 class ImplCommonButtonData
72 public:
73 ImplCommonButtonData();
75 tools::Rectangle maFocusRect;
76 long mnSeparatorX;
77 DrawButtonFlags mnButtonState;
78 bool mbSmallSymbol;
80 Image maImage;
81 ImageAlign meImageAlign;
82 SymbolAlign meSymbolAlign;
84 /** StatusListener. Updates the button as the slot state changes */
85 rtl::Reference<VclStatusListener<Button>> mpStatusListener;
88 ImplCommonButtonData::ImplCommonButtonData() : maFocusRect(), mnSeparatorX(0), mnButtonState(DrawButtonFlags::NONE),
89 mbSmallSymbol(false), maImage(), meImageAlign(ImageAlign::Top), meSymbolAlign(SymbolAlign::LEFT)
93 Button::Button( WindowType nType ) :
94 Control( nType ),
95 mpButtonData( std::make_unique<ImplCommonButtonData>() )
99 Button::~Button()
101 disposeOnce();
104 void Button::dispose()
106 if (mpButtonData->mpStatusListener.is())
107 mpButtonData->mpStatusListener->dispose();
108 Control::dispose();
111 void Button::SetCommandHandler(const OUString& aCommand)
113 maCommand = aCommand;
114 SetClickHdl( LINK( this, Button, dispatchCommandHandler) );
116 mpButtonData->mpStatusListener = new VclStatusListener<Button>(this, aCommand);
117 mpButtonData->mpStatusListener->startListening();
120 void Button::Click()
122 ImplCallEventListenersAndHandler( VclEventId::ButtonClick, [this] () { maClickHdl.Call(this); } );
125 OUString Button::GetStandardText(StandardButtonType eButton)
127 static const char* aResIdAry[static_cast<int>(StandardButtonType::Count)] =
129 // http://lists.freedesktop.org/archives/libreoffice/2013-January/044513.html
130 // Under windows we don't want accelerators on ok/cancel but do on other
131 // buttons
132 #ifdef _WIN32
133 SV_BUTTONTEXT_OK_NOMNEMONIC,
134 SV_BUTTONTEXT_CANCEL_NOMNEMONIC,
135 #else
136 SV_BUTTONTEXT_OK,
137 SV_BUTTONTEXT_CANCEL,
138 #endif
139 SV_BUTTONTEXT_YES,
140 SV_BUTTONTEXT_NO,
141 SV_BUTTONTEXT_RETRY,
142 SV_BUTTONTEXT_HELP,
143 SV_BUTTONTEXT_CLOSE,
144 SV_BUTTONTEXT_MORE,
145 SV_BUTTONTEXT_IGNORE,
146 SV_BUTTONTEXT_ABORT,
147 SV_BUTTONTEXT_LESS,
148 STR_WIZDLG_PREVIOUS,
149 STR_WIZDLG_NEXT,
150 STR_WIZDLG_FINISH,
153 return VclResId(aResIdAry[static_cast<sal_uInt16>(eButton)]);
156 void Button::SetModeImage( const Image& rImage )
158 if ( rImage != mpButtonData->maImage )
160 mpButtonData->maImage = rImage;
161 StateChanged( StateChangedType::Data );
162 queue_resize();
166 Image const & Button::GetModeImage( ) const
168 return mpButtonData->maImage;
171 bool Button::HasImage() const
173 return !!(mpButtonData->maImage);
176 void Button::SetImageAlign( ImageAlign eAlign )
178 if ( mpButtonData->meImageAlign != eAlign )
180 mpButtonData->meImageAlign = eAlign;
181 StateChanged( StateChangedType::Data );
185 ImageAlign Button::GetImageAlign() const
187 return mpButtonData->meImageAlign;
190 long Button::ImplGetSeparatorX() const
192 return mpButtonData->mnSeparatorX;
195 void Button::ImplSetSeparatorX( long nX )
197 mpButtonData->mnSeparatorX = nX;
200 DrawTextFlags Button::ImplGetTextStyle( WinBits nWinStyle, DrawFlags nDrawFlags )
202 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
203 DrawTextFlags nTextStyle = FixedText::ImplGetTextStyle(nWinStyle & ~WB_DEFBUTTON);
205 if (!IsEnabled())
206 nTextStyle |= DrawTextFlags::Disable;
208 if ((nDrawFlags & DrawFlags::Mono) ||
209 (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
211 nTextStyle |= DrawTextFlags::Mono;
214 return nTextStyle;
217 void Button::ImplDrawAlignedImage(OutputDevice* pDev, Point& rPos,
218 Size& rSize,
219 sal_uLong nImageSep,
220 DrawTextFlags nTextStyle, tools::Rectangle *pSymbolRect,
221 bool bAddImageSep)
223 OUString aText(GetText());
224 bool bDrawImage = HasImage();
225 bool bDrawText = !aText.isEmpty();
226 bool bHasSymbol = pSymbolRect != nullptr;
228 // No text and no image => nothing to do => return
229 if (!bDrawImage && !bDrawText && !bHasSymbol)
230 return;
232 WinBits nWinStyle = GetStyle();
233 tools::Rectangle aOutRect( rPos, rSize );
234 ImageAlign eImageAlign = mpButtonData->meImageAlign;
235 Size aImageSize = mpButtonData->maImage.GetSizePixel();
237 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
238 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
240 // Drawing text or symbol only is simple, use style and output rectangle
241 if (bHasSymbol && !bDrawImage && !bDrawText)
243 *pSymbolRect = aOutRect;
244 return;
246 else if (bDrawText && !bDrawImage && !bHasSymbol)
248 aOutRect = DrawControlText(*pDev, aOutRect, aText, nTextStyle, nullptr, nullptr);
250 ImplSetFocusRect(aOutRect);
251 rSize = aOutRect.GetSize();
252 rPos = aOutRect.TopLeft();
254 return;
257 // check for HC mode ( image only! )
258 Image* pImage = &(mpButtonData->maImage);
260 Size aTextSize;
261 Size aSymbolSize;
262 Size aDeviceTextSize;
263 Size aMax;
264 Point aImagePos = rPos;
265 Point aTextPos = rPos;
266 tools::Rectangle aUnion(aImagePos, aImageSize);
267 tools::Rectangle aSymbol;
268 long nSymbolHeight = 0;
270 if (bDrawText || bHasSymbol)
272 // Get the size of the text output area ( the symbol will be drawn in
273 // this area as well, so the symbol rectangle will be calculated here, too )
275 tools::Rectangle aRect(Point(), rSize);
276 Size aTSSize;
278 if (bHasSymbol)
280 if (bDrawText)
282 nSymbolHeight = pDev->GetTextHeight();
283 if (mpButtonData->mbSmallSymbol)
284 nSymbolHeight = nSymbolHeight * 3 / 4;
286 aSymbol = tools::Rectangle(Point(), Size(nSymbolHeight, nSymbolHeight));
287 ImplCalcSymbolRect(aSymbol);
288 aRect.AdjustLeft(3 * nSymbolHeight / 2 );
289 aTSSize.setWidth( 3 * nSymbolHeight / 2 );
291 else
293 aSymbol = tools::Rectangle(Point(), rSize);
294 ImplCalcSymbolRect(aSymbol);
295 aTSSize.setWidth( aSymbol.GetWidth() );
297 aTSSize.setHeight( aSymbol.GetHeight() );
298 aSymbolSize = aSymbol.GetSize();
301 if (bDrawText)
303 if ((eImageAlign == ImageAlign::LeftTop) ||
304 (eImageAlign == ImageAlign::Left ) ||
305 (eImageAlign == ImageAlign::LeftBottom) ||
306 (eImageAlign == ImageAlign::RightTop) ||
307 (eImageAlign == ImageAlign::Right) ||
308 (eImageAlign == ImageAlign::RightBottom))
310 aRect.AdjustRight( -sal_Int32(aImageSize.Width() + nImageSep) );
312 else if ((eImageAlign == ImageAlign::TopLeft) ||
313 (eImageAlign == ImageAlign::Top) ||
314 (eImageAlign == ImageAlign::TopRight) ||
315 (eImageAlign == ImageAlign::BottomLeft) ||
316 (eImageAlign == ImageAlign::Bottom) ||
317 (eImageAlign == ImageAlign::BottomRight))
319 aRect.AdjustBottom( -sal_Int32(aImageSize.Height() + nImageSep) );
322 aRect = GetControlTextRect(*pDev, aRect, aText, nTextStyle, &aDeviceTextSize);
323 aTextSize = aRect.GetSize();
325 aTSSize.AdjustWidth(aTextSize.Width() );
327 if (aTSSize.Height() < aTextSize.Height())
328 aTSSize.setHeight( aTextSize.Height() );
330 if (bAddImageSep && bDrawImage)
332 long nDiff = (aImageSize.Height() - aTextSize.Height()) / 3;
333 if (nDiff > 0)
334 nImageSep += nDiff;
338 aMax.setWidth( std::max(aTSSize.Width(), aImageSize.Width()) );
339 aMax.setHeight( std::max(aTSSize.Height(), aImageSize.Height()) );
341 // Now calculate the output area for the image and the text according to the image align flags
343 if ((eImageAlign == ImageAlign::Left) ||
344 (eImageAlign == ImageAlign::Right))
346 aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
347 aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
349 else if ((eImageAlign == ImageAlign::LeftBottom) ||
350 (eImageAlign == ImageAlign::RightBottom))
352 aImagePos.setY( rPos.Y() + aMax.Height() - aImageSize.Height() );
353 aTextPos.setY( rPos.Y() + aMax.Height() - aTSSize.Height() );
355 else if ((eImageAlign == ImageAlign::Top) ||
356 (eImageAlign == ImageAlign::Bottom))
358 aImagePos.setX( rPos.X() + (aMax.Width() - aImageSize.Width()) / 2 );
359 aTextPos.setX( rPos.X() + (aMax.Width() - aTSSize.Width()) / 2 );
361 else if ((eImageAlign == ImageAlign::TopRight) ||
362 (eImageAlign == ImageAlign::BottomRight))
364 aImagePos.setX( rPos.X() + aMax.Width() - aImageSize.Width() );
365 aTextPos.setX( rPos.X() + aMax.Width() - aTSSize.Width() );
368 if ((eImageAlign == ImageAlign::LeftTop) ||
369 (eImageAlign == ImageAlign::Left) ||
370 (eImageAlign == ImageAlign::LeftBottom))
372 aTextPos.setX( rPos.X() + aImageSize.Width() + nImageSep );
374 else if ((eImageAlign == ImageAlign::RightTop) ||
375 (eImageAlign == ImageAlign::Right) ||
376 (eImageAlign == ImageAlign::RightBottom))
378 aImagePos.setX( rPos.X() + aTSSize.Width() + nImageSep );
380 else if ((eImageAlign == ImageAlign::TopLeft) ||
381 (eImageAlign == ImageAlign::Top) ||
382 (eImageAlign == ImageAlign::TopRight))
384 aTextPos.setY( rPos.Y() + aImageSize.Height() + nImageSep );
386 else if ((eImageAlign == ImageAlign::BottomLeft) ||
387 (eImageAlign == ImageAlign::Bottom) ||
388 (eImageAlign == ImageAlign::BottomRight))
390 aImagePos.setY( rPos.Y() + aTSSize.Height() + nImageSep );
392 else if (eImageAlign == ImageAlign::Center)
394 aImagePos.setX( rPos.X() + (aMax.Width() - aImageSize.Width()) / 2 );
395 aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
396 aTextPos.setX( rPos.X() + (aMax.Width() - aTSSize.Width()) / 2 );
397 aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
399 aUnion = tools::Rectangle(aImagePos, aImageSize);
400 aUnion.Union(tools::Rectangle(aTextPos, aTSSize));
403 // Now place the combination of text and image in the output area of the button
404 // according to the window style (WinBits)
405 long nXOffset = 0;
406 long nYOffset = 0;
408 if (nWinStyle & WB_CENTER)
410 nXOffset = (rSize.Width() - aUnion.GetWidth()) / 2;
412 else if (nWinStyle & WB_RIGHT)
414 nXOffset = rSize.Width() - aUnion.GetWidth();
417 if (nWinStyle & WB_VCENTER)
419 nYOffset = (rSize.Height() - aUnion.GetHeight()) / 2;
421 else if (nWinStyle & WB_BOTTOM)
423 nYOffset = rSize.Height() - aUnion.GetHeight();
426 // the top left corner should always be visible, so we don't allow negative offsets
427 if (nXOffset < 0) nXOffset = 0;
428 if (nYOffset < 0) nYOffset = 0;
430 aImagePos.AdjustX(nXOffset );
431 aImagePos.AdjustY(nYOffset );
432 aTextPos.AdjustX(nXOffset );
433 aTextPos.AdjustY(nYOffset );
435 // set rPos and rSize to the union
436 rSize = aUnion.GetSize();
437 rPos.AdjustX(nXOffset );
438 rPos.AdjustY(nYOffset );
440 if (bHasSymbol)
442 if (mpButtonData->meSymbolAlign == SymbolAlign::RIGHT)
444 Point aRightPos(aTextPos.X() + aTextSize.Width() + aSymbolSize.Width() / 2, aTextPos.Y());
445 *pSymbolRect = tools::Rectangle(aRightPos, aSymbolSize);
447 else
449 *pSymbolRect = tools::Rectangle(aTextPos, aSymbolSize);
450 aTextPos.AdjustX(3 * nSymbolHeight / 2 );
452 if (mpButtonData->mbSmallSymbol)
454 nYOffset = (aUnion.GetHeight() - aSymbolSize.Height()) / 2;
455 pSymbolRect->setY(aTextPos.Y() + nYOffset);
459 DrawImageFlags nStyle = DrawImageFlags::NONE;
461 if (!IsEnabled())
463 nStyle |= DrawImageFlags::Disable;
466 if (IsZoom())
467 pDev->DrawImage(aImagePos, aImageSize, *pImage, nStyle);
468 else
469 pDev->DrawImage(aImagePos, *pImage, nStyle);
471 if (bDrawText)
473 const tools::Rectangle aTOutRect(aTextPos, aTextSize);
474 ImplSetFocusRect(aTOutRect);
475 DrawControlText(*pDev, aTOutRect, aText, nTextStyle, nullptr, nullptr, &aDeviceTextSize);
477 else
479 ImplSetFocusRect(tools::Rectangle(aImagePos, aImageSize));
483 void Button::ImplSetFocusRect(const tools::Rectangle &rFocusRect)
485 tools::Rectangle aFocusRect = rFocusRect;
486 tools::Rectangle aOutputRect(Point(), GetOutputSizePixel());
488 if (!aFocusRect.IsEmpty())
490 aFocusRect.AdjustLeft( -1 );
491 aFocusRect.AdjustTop( -1 );
492 aFocusRect.AdjustRight( 1 );
493 aFocusRect.AdjustBottom( 1 );
496 if (aFocusRect.Left() < aOutputRect.Left())
497 aFocusRect.SetLeft( aOutputRect.Left() );
498 if (aFocusRect.Top() < aOutputRect.Top())
499 aFocusRect.SetTop( aOutputRect.Top() );
500 if (aFocusRect.Right() > aOutputRect.Right())
501 aFocusRect.SetRight( aOutputRect.Right() );
502 if (aFocusRect.Bottom() > aOutputRect.Bottom())
503 aFocusRect.SetBottom( aOutputRect.Bottom() );
505 mpButtonData->maFocusRect = aFocusRect;
508 const tools::Rectangle& Button::ImplGetFocusRect() const
510 return mpButtonData->maFocusRect;
513 DrawButtonFlags& Button::ImplGetButtonState()
515 return mpButtonData->mnButtonState;
518 DrawButtonFlags Button::ImplGetButtonState() const
520 return mpButtonData->mnButtonState;
523 void Button::ImplSetSymbolAlign( SymbolAlign eAlign )
525 if ( mpButtonData->meSymbolAlign != eAlign )
527 mpButtonData->meSymbolAlign = eAlign;
528 StateChanged( StateChangedType::Data );
532 void Button::SetSmallSymbol()
534 mpButtonData->mbSmallSymbol = true;
537 bool Button::IsSmallSymbol () const
539 return mpButtonData->mbSmallSymbol;
542 bool Button::set_property(const OString &rKey, const OUString &rValue)
544 if (rKey == "image-position")
546 ImageAlign eAlign = ImageAlign::Left;
547 if (rValue == "left")
548 eAlign = ImageAlign::Left;
549 else if (rValue == "right")
550 eAlign = ImageAlign::Right;
551 else if (rValue == "top")
552 eAlign = ImageAlign::Top;
553 else if (rValue == "bottom")
554 eAlign = ImageAlign::Bottom;
555 SetImageAlign(eAlign);
557 else if (rKey == "focus-on-click")
559 WinBits nBits = GetStyle();
560 nBits &= ~WB_NOPOINTERFOCUS;
561 if (!toBool(rValue))
562 nBits |= WB_NOPOINTERFOCUS;
563 SetStyle(nBits);
565 else
566 return Control::set_property(rKey, rValue);
567 return true;
570 void Button::statusChanged(const css::frame::FeatureStateEvent& rEvent)
572 Enable(rEvent.IsEnabled);
575 FactoryFunction Button::GetUITestFactory() const
577 return ButtonUIObject::create;
580 IMPL_STATIC_LINK( Button, dispatchCommandHandler, Button*, pButton, void )
582 if (pButton == nullptr)
583 return;
585 comphelper::dispatchCommand(pButton->maCommand, uno::Sequence<beans::PropertyValue>());
588 void PushButton::ImplInitPushButtonData()
590 mpWindowImpl->mbPushButton = true;
592 meSymbol = SymbolType::DONTKNOW;
593 meState = TRISTATE_FALSE;
594 mnDDStyle = PushButtonDropdownStyle::NONE;
595 mbIsActive = false;
596 mbPressed = false;
597 mbIsAction = false;
600 namespace
602 vcl::Window* getPreviousSibling(vcl::Window const *pParent)
604 return pParent ? pParent->GetWindow(GetWindowType::LastChild) : nullptr;
608 void PushButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
610 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
611 Button::ImplInit( pParent, nStyle, nullptr );
613 if ( nStyle & WB_NOLIGHTBORDER )
614 ImplGetButtonState() |= DrawButtonFlags::NoLightBorder;
616 ImplInitSettings( true );
619 WinBits PushButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
621 if ( !(nStyle & WB_NOTABSTOP) )
622 nStyle |= WB_TABSTOP;
624 // if no alignment is given, default to "vertically centered". This is because since
625 // #i26046#, we respect the vertical alignment flags (previously we didn't completely),
626 // but we of course want to look as before when no vertical alignment is specified
627 if ( ( nStyle & ( WB_TOP | WB_VCENTER | WB_BOTTOM ) ) == 0 )
628 nStyle |= WB_VCENTER;
630 if ( !(nStyle & WB_NOGROUP) &&
631 (!pPrevWindow ||
632 ((pPrevWindow->GetType() != WindowType::PUSHBUTTON ) &&
633 (pPrevWindow->GetType() != WindowType::OKBUTTON ) &&
634 (pPrevWindow->GetType() != WindowType::CANCELBUTTON) &&
635 (pPrevWindow->GetType() != WindowType::HELPBUTTON )) ) )
636 nStyle |= WB_GROUP;
637 return nStyle;
640 const vcl::Font& PushButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
642 return _rStyle.GetPushButtonFont();
645 const Color& PushButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
647 return _rStyle.GetButtonTextColor();
650 void PushButton::ImplInitSettings( bool bBackground )
652 Button::ImplInitSettings();
654 if ( bBackground )
656 SetBackground();
657 // #i38498#: do not check for GetParent()->IsChildTransparentModeEnabled()
658 // otherwise the formcontrol button will be overdrawn due to ParentClipMode::NoClip
659 // for radio and checkbox this is ok as they should appear transparent in documents
660 if ( IsNativeControlSupported( ControlType::Pushbutton, ControlPart::Entire ) ||
661 (GetStyle() & WB_FLATBUTTON) != 0 )
663 EnableChildTransparentMode();
664 SetParentClipMode( ParentClipMode::NoClip );
665 SetPaintTransparent( true );
667 if ((GetStyle() & WB_FLATBUTTON) == 0)
668 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
669 else
670 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRectsForFlatButtons;
672 else
674 EnableChildTransparentMode( false );
675 SetParentClipMode();
676 SetPaintTransparent( false );
681 void PushButton::ImplDrawPushButtonFrame(vcl::RenderContext& rRenderContext,
682 tools::Rectangle& rRect, DrawButtonFlags nStyle)
684 if (!(GetStyle() & (WB_RECTSTYLE | WB_SMALLSTYLE)))
686 StyleSettings aStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
687 if (IsControlBackground())
688 aStyleSettings.Set3DColors(GetControlBackground());
691 DecorationView aDecoView(&rRenderContext);
692 if (IsControlBackground())
694 AllSettings aSettings = rRenderContext.GetSettings();
695 AllSettings aOldSettings = aSettings;
696 StyleSettings aStyleSettings = aSettings.GetStyleSettings();
697 aStyleSettings.Set3DColors(GetControlBackground());
698 aSettings.SetStyleSettings(aStyleSettings);
700 // Call OutputDevice::SetSettings() explicitly, as rRenderContext may
701 // be a vcl::Window in fact, and vcl::Window::SetSettings() will call
702 // Invalidate(), which is a problem, since we're in Paint().
703 rRenderContext.OutputDevice::SetSettings(aSettings);
704 rRect = aDecoView.DrawButton(rRect, nStyle);
705 rRenderContext.OutputDevice::SetSettings(aOldSettings);
707 else
708 rRect = aDecoView.DrawButton(rRect, nStyle);
711 bool PushButton::ImplHitTestPushButton( vcl::Window const * pDev,
712 const Point& rPos )
714 tools::Rectangle aTestRect( Point(), pDev->GetOutputSizePixel() );
716 return aTestRect.IsInside( rPos );
719 DrawTextFlags PushButton::ImplGetTextStyle( DrawFlags nDrawFlags ) const
721 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
723 DrawTextFlags nTextStyle = DrawTextFlags::Mnemonic | DrawTextFlags::MultiLine | DrawTextFlags::EndEllipsis;
725 if ( ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono ) ||
726 ( nDrawFlags & DrawFlags::Mono ) )
727 nTextStyle |= DrawTextFlags::Mono;
729 if ( GetStyle() & WB_WORDBREAK )
730 nTextStyle |= DrawTextFlags::WordBreak;
731 if ( GetStyle() & WB_NOLABEL )
732 nTextStyle &= ~DrawTextFlags::Mnemonic;
734 if ( GetStyle() & WB_LEFT )
735 nTextStyle |= DrawTextFlags::Left;
736 else if ( GetStyle() & WB_RIGHT )
737 nTextStyle |= DrawTextFlags::Right;
738 else
739 nTextStyle |= DrawTextFlags::Center;
741 if ( GetStyle() & WB_TOP )
742 nTextStyle |= DrawTextFlags::Top;
743 else if ( GetStyle() & WB_BOTTOM )
744 nTextStyle |= DrawTextFlags::Bottom;
745 else
746 nTextStyle |= DrawTextFlags::VCenter;
748 if ( !IsEnabled() )
749 nTextStyle |= DrawTextFlags::Disable;
751 return nTextStyle;
754 static void ImplDrawBtnDropDownArrow( OutputDevice* pDev,
755 long nX, long nY,
756 Color const & rColor, bool bBlack )
758 Color aOldLineColor = pDev->GetLineColor();
759 Color aOldFillColor = pDev->GetFillColor();
761 pDev->SetLineColor();
762 if ( bBlack )
763 pDev->SetFillColor( COL_BLACK );
764 else
765 pDev->SetFillColor( rColor );
766 pDev->DrawRect( tools::Rectangle( nX+0, nY+0, nX+6, nY+0 ) );
767 pDev->DrawRect( tools::Rectangle( nX+1, nY+1, nX+5, nY+1 ) );
768 pDev->DrawRect( tools::Rectangle( nX+2, nY+2, nX+4, nY+2 ) );
769 pDev->DrawRect( tools::Rectangle( nX+3, nY+3, nX+3, nY+3 ) );
770 if ( bBlack )
772 pDev->SetFillColor( rColor );
773 pDev->DrawRect( tools::Rectangle( nX+2, nY+1, nX+4, nY+1 ) );
774 pDev->DrawRect( tools::Rectangle( nX+3, nY+2, nX+3, nY+2 ) );
776 pDev->SetLineColor( aOldLineColor );
777 pDev->SetFillColor( aOldFillColor );
780 void PushButton::ImplDrawPushButtonContent(OutputDevice* pDev, DrawFlags nDrawFlags,
781 const tools::Rectangle& rRect, bool bMenuBtnSep,
782 DrawButtonFlags nButtonFlags)
784 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
785 tools::Rectangle aInRect = rRect;
786 Color aColor;
787 DrawTextFlags nTextStyle = ImplGetTextStyle( nDrawFlags );
788 DrawSymbolFlags nStyle;
790 if( aInRect.Right() < aInRect.Left() || aInRect.Bottom() < aInRect.Top() )
791 return; // nothing to do
793 pDev->Push( PushFlags::CLIPREGION );
794 pDev->IntersectClipRegion( aInRect );
796 if ( nDrawFlags & DrawFlags::Mono )
797 aColor = COL_BLACK;
798 #ifdef MACOSX
799 else if ((nButtonFlags & DrawButtonFlags::Default) && !(GetStyle() & WB_FLATBUTTON))
801 // Make text color white if the button is a default control on macOS.
802 // Without this you get a button with a blue background and blue text
803 // which stands out as not looking right on macOS where default buttons
804 // have white text and a blue background.
805 aColor = COL_WHITE;
807 #endif
808 else if( (nButtonFlags & DrawButtonFlags::Highlight) && IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
810 if (nButtonFlags & DrawButtonFlags::Pressed)
811 aColor = rStyleSettings.GetButtonPressedRolloverTextColor();
812 else
813 aColor = rStyleSettings.GetButtonRolloverTextColor();
815 else if ( IsControlForeground() )
816 aColor = GetControlForeground();
817 else if( nButtonFlags & DrawButtonFlags::Highlight )
819 if (nButtonFlags & DrawButtonFlags::Pressed)
820 aColor = rStyleSettings.GetButtonPressedRolloverTextColor();
821 else
822 aColor = rStyleSettings.GetButtonRolloverTextColor();
824 else
825 aColor = rStyleSettings.GetButtonTextColor();
827 pDev->SetTextColor( aColor );
829 if ( IsEnabled() )
830 nStyle = DrawSymbolFlags::NONE;
831 else
832 nStyle = DrawSymbolFlags::Disable;
834 Size aSize = rRect.GetSize();
835 Point aPos = rRect.TopLeft();
837 sal_uLong nImageSep = 1 + (pDev->GetTextHeight()-10)/2;
838 if( nImageSep < 1 )
839 nImageSep = 1;
840 if ( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
841 mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
843 long nSeparatorX = 0;
844 tools::Rectangle aSymbolRect = aInRect;
846 // calculate symbol size
847 long nSymbolSize = pDev->GetTextHeight() / 2 + 1;
849 nSeparatorX = aInRect.Right() - 2*nSymbolSize;
850 aSize.AdjustWidth( -(2*nSymbolSize) );
852 // center symbol rectangle in the separated area
853 aSymbolRect.AdjustRight( -(nSymbolSize/2) );
854 aSymbolRect.SetLeft( aSymbolRect.Right() - nSymbolSize );
856 ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
857 nTextStyle, nullptr, true );
859 long nDistance = (aSymbolRect.GetHeight() > 10) ? 2 : 1;
860 DecorationView aDecoView( pDev );
861 if( bMenuBtnSep && nSeparatorX > 0 )
863 Point aStartPt( nSeparatorX, aSymbolRect.Top()+nDistance );
864 Point aEndPt( nSeparatorX, aSymbolRect.Bottom()-nDistance );
865 aDecoView.DrawSeparator( aStartPt, aEndPt );
867 ImplSetSeparatorX( nSeparatorX );
869 aDecoView.DrawSymbol( aSymbolRect, SymbolType::SPIN_DOWN, aColor, nStyle );
872 else
874 tools::Rectangle aSymbolRect;
875 ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
876 nTextStyle, IsSymbol() ? &aSymbolRect : nullptr, true );
878 if ( IsSymbol() )
880 DecorationView aDecoView( pDev );
881 aDecoView.DrawSymbol( aSymbolRect, meSymbol, aColor, nStyle );
884 if ( mnDDStyle == PushButtonDropdownStyle::Toolbox )
886 bool bBlack = false;
887 Color aArrowColor( COL_BLACK );
889 if ( !(nDrawFlags & DrawFlags::Mono) )
891 if ( !IsEnabled() )
892 aArrowColor = rStyleSettings.GetShadowColor();
893 else
895 aArrowColor = COL_LIGHTGREEN;
896 bBlack = true;
900 ImplDrawBtnDropDownArrow( pDev, aInRect.Right()-6, aInRect.Top()+1,
901 aArrowColor, bBlack );
905 pDev->Pop(); // restore clipregion
908 void PushButton::ImplDrawPushButton(vcl::RenderContext& rRenderContext)
910 HideFocus();
912 DrawButtonFlags nButtonStyle = ImplGetButtonState();
913 Size aOutSz(GetOutputSizePixel());
914 tools::Rectangle aRect(Point(), aOutSz);
915 tools::Rectangle aInRect = aRect;
916 bool bNativeOK = false;
918 // adjust style if button should be rendered 'pressed'
919 if (mbPressed || mbIsActive)
920 nButtonStyle |= DrawButtonFlags::Pressed;
922 // TODO: move this to Window class or make it a member !!!
923 ControlType aCtrlType = ControlType::Generic;
924 switch(GetParent()->GetType())
926 case WindowType::LISTBOX:
927 case WindowType::MULTILISTBOX:
928 case WindowType::TREELISTBOX:
929 aCtrlType = ControlType::Listbox;
930 break;
932 case WindowType::COMBOBOX:
933 case WindowType::PATTERNBOX:
934 case WindowType::NUMERICBOX:
935 case WindowType::METRICBOX:
936 case WindowType::CURRENCYBOX:
937 case WindowType::DATEBOX:
938 case WindowType::TIMEBOX:
939 case WindowType::LONGCURRENCYBOX:
940 aCtrlType = ControlType::Combobox;
941 break;
942 default:
943 break;
946 bool bDropDown = (IsSymbol() && (GetSymbol() == SymbolType::SPIN_DOWN) && GetText().isEmpty());
948 if( bDropDown && (aCtrlType == ControlType::Combobox || aCtrlType == ControlType::Listbox))
950 if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::Entire))
952 // skip painting if the button was already drawn by the theme
953 if (aCtrlType == ControlType::Combobox)
955 Edit* pEdit = static_cast<Edit*>(GetParent());
956 if (pEdit->ImplUseNativeBorder(rRenderContext, pEdit->GetStyle()))
957 bNativeOK = true;
959 else if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::HasBackgroundTexture))
961 bNativeOK = true;
964 if (!bNativeOK && GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::ButtonDown))
966 // let the theme draw it, note we then need support
967 // for ControlType::Listbox/ControlPart::ButtonDown and ControlType::Combobox/ControlPart::ButtonDown
969 ImplControlValue aControlValue;
970 ControlState nState = ControlState::NONE;
972 if (mbPressed || mbIsActive)
973 nState |= ControlState::PRESSED;
974 if (ImplGetButtonState() & DrawButtonFlags::Pressed)
975 nState |= ControlState::PRESSED;
976 if (HasFocus())
977 nState |= ControlState::FOCUSED;
978 if (ImplGetButtonState() & DrawButtonFlags::Default)
979 nState |= ControlState::DEFAULT;
980 if (Window::IsEnabled())
981 nState |= ControlState::ENABLED;
983 if (IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()))
984 nState |= ControlState::ROLLOVER;
986 if ( IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()) && mbIsActive)
988 nState |= ControlState::ROLLOVER;
989 nButtonStyle &= ~DrawButtonFlags::Pressed;
992 bNativeOK = rRenderContext.DrawNativeControl(aCtrlType, ControlPart::ButtonDown, aInRect, nState,
993 aControlValue, OUString());
998 if (bNativeOK)
999 return;
1001 bool bRollOver = (IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()));
1002 if (bRollOver)
1003 nButtonStyle |= DrawButtonFlags::Highlight;
1004 bool bDrawMenuSep = mnDDStyle == PushButtonDropdownStyle::SplitMenuButton;
1005 if (GetStyle() & WB_FLATBUTTON)
1007 if (!bRollOver && !HasFocus())
1008 bDrawMenuSep = false;
1010 // tdf#123175 if there is a custom control bg set, draw the button without outsourcing to the NWF
1011 bNativeOK = !IsControlBackground() && rRenderContext.IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire);
1012 if (bNativeOK)
1014 PushButtonValue aControlValue;
1015 aControlValue.mbIsAction = isAction();
1017 tools::Rectangle aCtrlRegion(aInRect);
1018 ControlState nState = ControlState::NONE;
1020 if (mbPressed || IsChecked() || mbIsActive)
1022 nState |= ControlState::PRESSED;
1023 nButtonStyle |= DrawButtonFlags::Pressed;
1025 if (ImplGetButtonState() & DrawButtonFlags::Pressed)
1026 nState |= ControlState::PRESSED;
1027 if (HasFocus())
1028 nState |= ControlState::FOCUSED;
1029 if (ImplGetButtonState() & DrawButtonFlags::Default)
1030 nState |= ControlState::DEFAULT;
1031 if (Window::IsEnabled())
1032 nState |= ControlState::ENABLED;
1034 if (bRollOver || mbIsActive)
1036 nButtonStyle |= DrawButtonFlags::Highlight;
1037 nState |= ControlState::ROLLOVER;
1040 if (mbIsActive && bRollOver)
1042 nState &= ~ControlState::PRESSED;
1043 nButtonStyle &= ~DrawButtonFlags::Pressed;
1046 if (GetStyle() & WB_BEVELBUTTON)
1047 aControlValue.mbBevelButton = true;
1049 // draw frame into invisible window to have aInRect modified correctly
1050 // but do not shift the inner rect for pressed buttons (ie remove DrawButtonFlags::Pressed)
1051 // this assumes the theme has enough visual cues to signalize the button was pressed
1052 //Window aWin( this );
1053 //ImplDrawPushButtonFrame( &aWin, aInRect, nButtonStyle & ~DrawButtonFlags::Pressed );
1055 // looks better this way as symbols were displaced slightly using the above approach
1056 aInRect.AdjustTop(4 );
1057 aInRect.AdjustBottom( -4 );
1058 aInRect.AdjustLeft(4 );
1059 aInRect.AdjustRight( -4 );
1061 // prepare single line hint (needed on mac to decide between normal push button and
1062 // rectangular bevel button look)
1063 Size aFontSize(Application::GetSettings().GetStyleSettings().GetPushButtonFont().GetFontSize());
1064 aFontSize = rRenderContext.LogicToPixel(aFontSize, MapMode(MapUnit::MapPoint));
1065 Size aInRectSize(rRenderContext.LogicToPixel(Size(aInRect.GetWidth(), aInRect.GetHeight())));
1066 aControlValue.mbSingleLine = (aInRectSize.Height() < 2 * aFontSize.Height());
1068 if ((nState & ControlState::ROLLOVER) || !(GetStyle() & WB_FLATBUTTON))
1070 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion, nState,
1071 aControlValue, OUString() /*PushButton::GetText()*/);
1073 else
1075 bNativeOK = true;
1078 // draw content using the same aInRect as non-native VCL would do
1079 ImplDrawPushButtonContent(&rRenderContext, DrawFlags::NONE,
1080 aInRect, bDrawMenuSep, nButtonStyle);
1082 if (HasFocus())
1083 ShowFocus(ImplGetFocusRect());
1086 if (!bNativeOK)
1088 // draw PushButtonFrame, aInRect has content size afterwards
1089 if (GetStyle() & WB_FLATBUTTON)
1091 tools::Rectangle aTempRect(aInRect);
1092 if (bRollOver)
1093 ImplDrawPushButtonFrame(rRenderContext, aTempRect, nButtonStyle);
1094 aInRect.AdjustLeft(2 );
1095 aInRect.AdjustTop(2 );
1096 aInRect.AdjustRight( -2 );
1097 aInRect.AdjustBottom( -2 );
1099 else
1101 ImplDrawPushButtonFrame(rRenderContext, aInRect, nButtonStyle);
1104 // draw content
1105 ImplDrawPushButtonContent(&rRenderContext, DrawFlags::NONE, aInRect, bDrawMenuSep, nButtonStyle);
1107 if (HasFocus())
1109 ShowFocus(ImplGetFocusRect());
1114 void PushButton::ImplSetDefButton( bool bSet )
1116 Size aSize( GetSizePixel() );
1117 Point aPos( GetPosPixel() );
1118 int dLeft(0), dRight(0), dTop(0), dBottom(0);
1119 bool bSetPos = false;
1121 if ( IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
1123 tools::Rectangle aBound, aCont;
1124 tools::Rectangle aCtrlRegion( 0, 0, 80, 20 ); // use a constant size to avoid accumulating
1125 // will not work if the theme has dynamic adornment sizes
1126 ImplControlValue aControlValue;
1128 // get native size of a 'default' button
1129 // and adjust the VCL button if more space for adornment is required
1130 if( GetNativeControlRegion( ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion,
1131 ControlState::DEFAULT|ControlState::ENABLED,
1132 aControlValue,
1133 aBound, aCont ) )
1135 dLeft = aCont.Left() - aBound.Left();
1136 dTop = aCont.Top() - aBound.Top();
1137 dRight = aBound.Right() - aCont.Right();
1138 dBottom = aBound.Bottom() - aCont.Bottom();
1139 bSetPos = dLeft || dTop || dRight || dBottom;
1143 if ( bSet )
1145 if( !(ImplGetButtonState() & DrawButtonFlags::Default) && bSetPos )
1147 // adjust pos/size when toggling from non-default to default
1148 aPos.Move(-dLeft, -dTop);
1149 aSize.AdjustWidth(dLeft + dRight );
1150 aSize.AdjustHeight(dTop + dBottom );
1152 ImplGetButtonState() |= DrawButtonFlags::Default;
1154 else
1156 if( (ImplGetButtonState() & DrawButtonFlags::Default) && bSetPos )
1158 // adjust pos/size when toggling from default to non-default
1159 aPos.Move(dLeft, dTop);
1160 aSize.AdjustWidth( -(dLeft + dRight) );
1161 aSize.AdjustHeight( -(dTop + dBottom) );
1163 ImplGetButtonState() &= ~DrawButtonFlags::Default;
1165 if( bSetPos )
1166 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
1168 Invalidate();
1171 bool PushButton::ImplIsDefButton() const
1173 return bool(ImplGetButtonState() & DrawButtonFlags::Default);
1176 PushButton::PushButton( WindowType nType ) :
1177 Button( nType )
1179 ImplInitPushButtonData();
1182 PushButton::PushButton( vcl::Window* pParent, WinBits nStyle ) :
1183 Button( WindowType::PUSHBUTTON )
1185 ImplInitPushButtonData();
1186 ImplInit( pParent, nStyle );
1189 void PushButton::MouseButtonDown( const MouseEvent& rMEvt )
1191 if ( rMEvt.IsLeft() &&
1192 ImplHitTestPushButton( this, rMEvt.GetPosPixel() ) )
1194 StartTrackingFlags nTrackFlags = StartTrackingFlags::NONE;
1196 if ( ( GetStyle() & WB_REPEAT ) &&
1197 ! ( GetStyle() & WB_TOGGLE ) )
1198 nTrackFlags |= StartTrackingFlags::ButtonRepeat;
1200 ImplGetButtonState() |= DrawButtonFlags::Pressed;
1201 Invalidate();
1202 StartTracking( nTrackFlags );
1204 if ( nTrackFlags & StartTrackingFlags::ButtonRepeat )
1205 Click();
1209 void PushButton::Tracking( const TrackingEvent& rTEvt )
1211 if ( rTEvt.IsTrackingEnded() )
1213 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
1215 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
1216 GrabFocus();
1218 if ( GetStyle() & WB_TOGGLE )
1220 // Don't toggle, when aborted
1221 if ( !rTEvt.IsTrackingCanceled() )
1223 if ( IsChecked() )
1225 Check( false );
1226 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
1228 else
1229 Check();
1232 else
1233 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
1235 Invalidate();
1237 // do not call Click handler if aborted
1238 if ( !rTEvt.IsTrackingCanceled() )
1240 if ( ! ( ( GetStyle() & WB_REPEAT ) &&
1241 ! ( GetStyle() & WB_TOGGLE ) ) )
1242 Click();
1246 else
1248 if ( ImplHitTestPushButton( this, rTEvt.GetMouseEvent().GetPosPixel() ) )
1250 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
1252 if ( rTEvt.IsTrackingRepeat() && (GetStyle() & WB_REPEAT) &&
1253 ! ( GetStyle() & WB_TOGGLE ) )
1254 Click();
1256 else
1258 ImplGetButtonState() |= DrawButtonFlags::Pressed;
1259 Invalidate();
1262 else
1264 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
1266 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
1267 Invalidate();
1273 void PushButton::KeyInput( const KeyEvent& rKEvt )
1275 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1277 if ( !aKeyCode.GetModifier() &&
1278 ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
1280 if ( !(ImplGetButtonState() & DrawButtonFlags::Pressed) )
1282 ImplGetButtonState() |= DrawButtonFlags::Pressed;
1283 Invalidate();
1286 if ( ( GetStyle() & WB_REPEAT ) &&
1287 ! ( GetStyle() & WB_TOGGLE ) )
1288 Click();
1290 else if ( (ImplGetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
1292 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
1293 Invalidate();
1295 else
1296 Button::KeyInput( rKEvt );
1299 void PushButton::KeyUp( const KeyEvent& rKEvt )
1301 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1303 if ( (ImplGetButtonState() & DrawButtonFlags::Pressed) &&
1304 ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
1306 if ( GetStyle() & WB_TOGGLE )
1308 if ( IsChecked() )
1310 Check( false );
1311 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
1313 else
1314 Check();
1316 Toggle();
1318 else
1319 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
1321 Invalidate();
1323 if ( !( ( GetStyle() & WB_REPEAT ) &&
1324 ! ( GetStyle() & WB_TOGGLE ) ) )
1325 Click();
1327 else
1328 Button::KeyUp( rKEvt );
1331 void PushButton::FillLayoutData() const
1333 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1334 const_cast<PushButton*>(this)->Invalidate();
1337 void PushButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1339 ImplDrawPushButton(rRenderContext);
1342 void PushButton::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize,
1343 DrawFlags nFlags )
1345 Point aPos = pDev->LogicToPixel( rPos );
1346 Size aSize = pDev->LogicToPixel( rSize );
1347 tools::Rectangle aRect( aPos, aSize );
1348 vcl::Font aFont = GetDrawPixelFont( pDev );
1350 pDev->Push();
1351 pDev->SetMapMode();
1352 pDev->SetFont( aFont );
1353 if ( nFlags & DrawFlags::Mono )
1355 pDev->SetTextColor( COL_BLACK );
1357 else
1359 pDev->SetTextColor( GetTextColor() );
1361 // DecoView uses the FaceColor...
1362 AllSettings aSettings = pDev->GetSettings();
1363 StyleSettings aStyleSettings = aSettings.GetStyleSettings();
1364 if ( IsControlBackground() )
1365 aStyleSettings.SetFaceColor( GetControlBackground() );
1366 else
1367 aStyleSettings.SetFaceColor( GetSettings().GetStyleSettings().GetFaceColor() );
1368 aSettings.SetStyleSettings( aStyleSettings );
1369 pDev->OutputDevice::SetSettings( aSettings );
1371 pDev->SetTextFillColor();
1373 DecorationView aDecoView( pDev );
1374 DrawButtonFlags nButtonStyle = DrawButtonFlags::NONE;
1375 if ( nFlags & DrawFlags::Mono )
1376 nButtonStyle |= DrawButtonFlags::Mono;
1377 if ( IsChecked() )
1378 nButtonStyle |= DrawButtonFlags::Checked;
1379 aRect = aDecoView.DrawButton( aRect, nButtonStyle );
1381 ImplDrawPushButtonContent( pDev, nFlags, aRect, true, nButtonStyle );
1382 pDev->Pop();
1385 void PushButton::Resize()
1387 Control::Resize();
1388 Invalidate();
1391 void PushButton::GetFocus()
1393 ShowFocus( ImplGetFocusRect() );
1394 SetInputContext( InputContext( GetFont() ) );
1395 Button::GetFocus();
1398 void PushButton::LoseFocus()
1400 EndSelection();
1401 HideFocus();
1402 Button::LoseFocus();
1405 void PushButton::StateChanged( StateChangedType nType )
1407 Button::StateChanged( nType );
1409 if ( (nType == StateChangedType::Enable) ||
1410 (nType == StateChangedType::Text) ||
1411 (nType == StateChangedType::Data) ||
1412 (nType == StateChangedType::State) ||
1413 (nType == StateChangedType::UpdateMode) )
1415 if ( IsReallyVisible() && IsUpdateMode() )
1416 Invalidate();
1418 else if ( nType == StateChangedType::Style )
1420 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
1422 bool bIsDefButton = ( GetStyle() & WB_DEFBUTTON ) != 0;
1423 bool bWasDefButton = ( GetPrevStyle() & WB_DEFBUTTON ) != 0;
1424 if ( bIsDefButton != bWasDefButton )
1425 ImplSetDefButton( bIsDefButton );
1427 if ( IsReallyVisible() && IsUpdateMode() )
1429 if ( (GetPrevStyle() & PUSHBUTTON_VIEW_STYLE) !=
1430 (GetStyle() & PUSHBUTTON_VIEW_STYLE) )
1431 Invalidate();
1434 else if ( (nType == StateChangedType::Zoom) ||
1435 (nType == StateChangedType::ControlFont) )
1437 ImplInitSettings( false );
1438 Invalidate();
1440 else if ( nType == StateChangedType::ControlForeground )
1442 ImplInitSettings( false );
1443 Invalidate();
1445 else if ( nType == StateChangedType::ControlBackground )
1447 ImplInitSettings( true );
1448 Invalidate();
1452 void PushButton::DataChanged( const DataChangedEvent& rDCEvt )
1454 Button::DataChanged( rDCEvt );
1456 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1457 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1458 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1459 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1461 ImplInitSettings( true );
1462 Invalidate();
1466 bool PushButton::PreNotify( NotifyEvent& rNEvt )
1468 const MouseEvent* pMouseEvt = nullptr;
1470 if( (rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != nullptr )
1472 if( pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow() )
1474 // trigger redraw as mouse over state has changed
1476 // TODO: move this to Window class or make it a member !!!
1477 ControlType aCtrlType = ControlType::Generic;
1478 switch( GetParent()->GetType() )
1480 case WindowType::LISTBOX:
1481 case WindowType::MULTILISTBOX:
1482 case WindowType::TREELISTBOX:
1483 aCtrlType = ControlType::Listbox;
1484 break;
1486 case WindowType::COMBOBOX:
1487 case WindowType::PATTERNBOX:
1488 case WindowType::NUMERICBOX:
1489 case WindowType::METRICBOX:
1490 case WindowType::CURRENCYBOX:
1491 case WindowType::DATEBOX:
1492 case WindowType::TIMEBOX:
1493 case WindowType::LONGCURRENCYBOX:
1494 aCtrlType = ControlType::Combobox;
1495 break;
1496 default:
1497 break;
1500 bool bDropDown = ( IsSymbol() && (GetSymbol()==SymbolType::SPIN_DOWN) && GetText().isEmpty() );
1502 if( bDropDown && GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::Entire) &&
1503 !GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::ButtonDown) )
1505 vcl::Window *pBorder = GetParent()->GetWindow( GetWindowType::Border );
1506 if(aCtrlType == ControlType::Combobox)
1508 // only paint the button part to avoid flickering of the combobox text
1509 tools::Rectangle aClipRect( Point(), GetOutputSizePixel() );
1510 aClipRect.SetPos(pBorder->ScreenToOutputPixel(OutputToScreenPixel(aClipRect.TopLeft())));
1511 pBorder->Invalidate( aClipRect );
1513 else
1515 pBorder->Invalidate( InvalidateFlags::NoErase );
1516 pBorder->Update();
1519 else if( (GetStyle() & WB_FLATBUTTON) ||
1520 IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
1522 Invalidate();
1527 return Button::PreNotify(rNEvt);
1530 void PushButton::Toggle()
1532 ImplCallEventListenersAndHandler( VclEventId::PushbuttonToggle, nullptr );
1535 void PushButton::SetSymbol( SymbolType eSymbol )
1537 if ( meSymbol != eSymbol )
1539 meSymbol = eSymbol;
1540 CompatStateChanged( StateChangedType::Data );
1544 void PushButton::SetSymbolAlign( SymbolAlign eAlign )
1546 ImplSetSymbolAlign( eAlign );
1549 void PushButton::SetDropDown( PushButtonDropdownStyle nStyle )
1551 if ( mnDDStyle != nStyle )
1553 mnDDStyle = nStyle;
1554 CompatStateChanged( StateChangedType::Data );
1558 void PushButton::SetState( TriState eState )
1560 if ( meState != eState )
1562 meState = eState;
1563 if ( meState == TRISTATE_FALSE )
1564 ImplGetButtonState() &= ~DrawButtonFlags(DrawButtonFlags::Checked | DrawButtonFlags::DontKnow);
1565 else if ( meState == TRISTATE_TRUE )
1567 ImplGetButtonState() &= ~DrawButtonFlags::DontKnow;
1568 ImplGetButtonState() |= DrawButtonFlags::Checked;
1570 else // TRISTATE_INDET
1572 ImplGetButtonState() &= ~DrawButtonFlags::Checked;
1573 ImplGetButtonState() |= DrawButtonFlags::DontKnow;
1576 CompatStateChanged( StateChangedType::State );
1577 Toggle();
1581 void PushButton::statusChanged(const css::frame::FeatureStateEvent& rEvent)
1583 Button::statusChanged(rEvent);
1584 if (rEvent.State.has<bool>())
1585 SetPressed(rEvent.State.get<bool>());
1588 void PushButton::SetPressed( bool bPressed )
1590 if ( mbPressed != bPressed )
1592 mbPressed = bPressed;
1593 CompatStateChanged( StateChangedType::Data );
1597 void PushButton::EndSelection()
1599 EndTracking( TrackingEventFlags::Cancel );
1600 if ( !IsDisposed() &&
1601 ImplGetButtonState() & DrawButtonFlags::Pressed )
1603 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
1604 if ( !mbPressed )
1605 Invalidate();
1609 Size PushButton::CalcMinimumSize() const
1611 Size aSize;
1613 if ( IsSymbol() )
1615 if ( IsSmallSymbol ())
1616 aSize = Size( 16, 12 );
1617 else
1618 aSize = Size( 26, 24 );
1620 else if ( Button::HasImage() )
1621 aSize = GetModeImage().GetSizePixel();
1622 if( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
1623 mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
1625 long nSymbolSize = GetTextHeight() / 2 + 1;
1626 aSize.AdjustWidth(2*nSymbolSize );
1628 if (!PushButton::GetText().isEmpty())
1630 Size textSize = GetTextRect( tools::Rectangle( Point(), Size( 0x7fffffff, 0x7fffffff ) ),
1631 PushButton::GetText(), ImplGetTextStyle( DrawFlags::NONE ) ).GetSize();
1632 aSize.AdjustWidth(textSize.Width() );
1633 aSize.setHeight( std::max( aSize.Height(), long( textSize.Height() * 1.15 ) ) );
1636 // cf. ImplDrawPushButton ...
1637 if( (GetStyle() & WB_SMALLSTYLE) == 0 )
1639 aSize.AdjustWidth(24 );
1640 aSize.AdjustHeight(12 );
1643 return CalcWindowSize( aSize );
1646 Size PushButton::GetOptimalSize() const
1648 return CalcMinimumSize();
1651 bool PushButton::set_property(const OString &rKey, const OUString &rValue)
1653 if (rKey == "has-default")
1655 WinBits nBits = GetStyle();
1656 nBits &= ~WB_DEFBUTTON;
1657 if (toBool(rValue))
1658 nBits |= WB_DEFBUTTON;
1659 SetStyle(nBits);
1661 else
1662 return Button::set_property(rKey, rValue);
1663 return true;
1666 void PushButton::ShowFocus(const tools::Rectangle& rRect)
1668 if (IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
1670 PushButtonValue aControlValue;
1671 aControlValue.mbIsAction = isAction();
1672 tools::Rectangle aInRect(Point(), GetOutputSizePixel());
1673 GetOutDev()->DrawNativeControl(ControlType::Pushbutton, ControlPart::Focus, aInRect,
1674 ControlState::FOCUSED, aControlValue, OUString());
1676 Button::ShowFocus(rRect);
1679 void OKButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1681 set_id("ok");
1682 PushButton::ImplInit( pParent, nStyle );
1684 SetText( Button::GetStandardText( StandardButtonType::OK ) );
1687 OKButton::OKButton( vcl::Window* pParent, WinBits nStyle ) :
1688 PushButton( WindowType::OKBUTTON )
1690 ImplInit( pParent, nStyle );
1693 void OKButton::Click()
1695 // close parent if no link set
1696 if ( !GetClickHdl() )
1698 vcl::Window* pParent = getNonLayoutParent(this);
1699 if ( pParent->IsSystemWindow() )
1701 if ( pParent->IsDialog() )
1703 if ( static_cast<Dialog*>(pParent)->IsInExecute() )
1704 static_cast<Dialog*>(pParent)->EndDialog( RET_OK );
1705 // prevent recursive calls
1706 else if ( !static_cast<Dialog*>(pParent)->IsInClose() )
1708 if ( pParent->GetStyle() & WB_CLOSEABLE )
1709 static_cast<Dialog*>(pParent)->Close();
1712 else
1714 if ( pParent->GetStyle() & WB_CLOSEABLE )
1715 static_cast<SystemWindow*>(pParent)->Close();
1719 else
1721 PushButton::Click();
1725 void CancelButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1727 set_id("cancel");
1728 PushButton::ImplInit( pParent, nStyle );
1730 SetText( Button::GetStandardText( StandardButtonType::Cancel ) );
1733 CancelButton::CancelButton( vcl::Window* pParent, WinBits nStyle ) :
1734 PushButton( WindowType::CANCELBUTTON )
1736 ImplInit( pParent, nStyle );
1739 void CancelButton::Click()
1741 // close parent if link not set
1742 if ( !GetClickHdl() )
1744 vcl::Window* pParent = getNonLayoutParent(this);
1745 if ( pParent->IsSystemWindow() )
1747 if ( pParent->IsDialog() )
1749 if ( static_cast<Dialog*>(pParent)->IsInExecute() )
1750 static_cast<Dialog*>(pParent)->EndDialog();
1751 // prevent recursive calls
1752 else if ( !static_cast<Dialog*>(pParent)->IsInClose() )
1754 if ( pParent->GetStyle() & WB_CLOSEABLE )
1755 static_cast<Dialog*>(pParent)->Close();
1758 else
1760 if ( pParent->GetStyle() & WB_CLOSEABLE )
1761 static_cast<SystemWindow*>(pParent)->Close();
1765 else
1767 PushButton::Click();
1771 CloseButton::CloseButton( vcl::Window* pParent, WinBits nStyle )
1772 : CancelButton(pParent, nStyle)
1774 SetText( Button::GetStandardText( StandardButtonType::Close ) );
1777 void HelpButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1779 set_id("help");
1780 PushButton::ImplInit( pParent, nStyle | WB_NOPOINTERFOCUS );
1782 SetText( Button::GetStandardText( StandardButtonType::Help ) );
1785 HelpButton::HelpButton( vcl::Window* pParent, WinBits nStyle ) :
1786 PushButton( WindowType::HELPBUTTON )
1788 ImplInit( pParent, nStyle );
1791 void HelpButton::Click()
1793 // trigger help if no link set
1794 if ( !GetClickHdl() )
1796 vcl::Window* pFocusWin = Application::GetFocusWindow();
1797 if ( !pFocusWin )
1798 pFocusWin = this;
1800 HelpEvent aEvt( pFocusWin->GetPointerPosPixel(), HelpEventMode::CONTEXT );
1801 pFocusWin->RequestHelp( aEvt );
1803 PushButton::Click();
1806 void HelpButton::StateChanged( StateChangedType nStateChange )
1808 // Hide when we have no help URL.
1809 if (comphelper::LibreOfficeKit::isActive() &&
1810 officecfg::Office::Common::Help::HelpRootURL::get().isEmpty())
1811 Hide();
1812 else
1813 PushButton::StateChanged(nStateChange);
1816 void RadioButton::ImplInitRadioButtonData()
1818 mbChecked = false;
1819 mbRadioCheck = true;
1820 mbStateChanged = false;
1823 void RadioButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1825 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
1826 Button::ImplInit( pParent, nStyle, nullptr );
1828 ImplInitSettings( true );
1831 WinBits RadioButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
1833 if ( !(nStyle & WB_NOGROUP) &&
1834 (!pPrevWindow || (pPrevWindow->GetType() != WindowType::RADIOBUTTON)) )
1835 nStyle |= WB_GROUP;
1836 if ( !(nStyle & WB_NOTABSTOP) )
1837 nStyle |= WB_TABSTOP;
1839 if ( IsChecked() && IsRadioCheckEnabled() )
1840 ImplUncheckAllOther( /*bSetStyle=*/false );
1842 return nStyle;
1845 const vcl::Font& RadioButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
1847 return _rStyle.GetRadioCheckFont();
1850 const Color& RadioButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
1852 return _rStyle.GetRadioCheckTextColor();
1855 void RadioButton::ImplInitSettings( bool bBackground )
1857 Button::ImplInitSettings();
1859 if ( bBackground )
1861 vcl::Window* pParent = GetParent();
1862 if ( !IsControlBackground() &&
1863 (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) ) )
1865 EnableChildTransparentMode();
1866 SetParentClipMode( ParentClipMode::NoClip );
1867 SetPaintTransparent( true );
1868 SetBackground();
1869 if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
1870 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
1872 else
1874 EnableChildTransparentMode( false );
1875 SetParentClipMode();
1876 SetPaintTransparent( false );
1878 if ( IsControlBackground() )
1879 SetBackground( GetControlBackground() );
1880 else
1881 SetBackground( pParent->GetBackground() );
1886 void RadioButton::ImplDrawRadioButtonState(vcl::RenderContext& rRenderContext)
1888 bool bNativeOK = false;
1890 // no native drawing for image radio buttons
1891 if (!maImage && rRenderContext.IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire))
1893 ImplControlValue aControlValue( mbChecked ? ButtonValue::On : ButtonValue::Off );
1894 tools::Rectangle aCtrlRect(maStateRect.TopLeft(), maStateRect.GetSize());
1895 ControlState nState = ControlState::NONE;
1897 if (ImplGetButtonState() & DrawButtonFlags::Pressed)
1898 nState |= ControlState::PRESSED;
1899 if (HasFocus())
1900 nState |= ControlState::FOCUSED;
1901 if (ImplGetButtonState() & DrawButtonFlags::Default)
1902 nState |= ControlState::DEFAULT;
1903 if (IsEnabled())
1904 nState |= ControlState::ENABLED;
1906 if (IsMouseOver() && maMouseRect.IsInside(GetPointerPosPixel()))
1907 nState |= ControlState::ROLLOVER;
1909 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Radiobutton, ControlPart::Entire, aCtrlRect,
1910 nState, aControlValue, OUString());
1913 if (!bNativeOK)
1915 if (!maImage)
1917 DrawButtonFlags nStyle = ImplGetButtonState();
1918 if (!IsEnabled())
1919 nStyle |= DrawButtonFlags::Disabled;
1920 if (mbChecked)
1921 nStyle |= DrawButtonFlags::Checked;
1922 Image aImage = GetRadioImage(rRenderContext.GetSettings(), nStyle);
1923 if (IsZoom())
1924 rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
1925 else
1926 rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
1928 else
1930 HideFocus();
1932 DecorationView aDecoView(&rRenderContext);
1933 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1934 tools::Rectangle aImageRect = maStateRect;
1935 Size aImageSize = maImage.GetSizePixel();
1936 bool bEnabled = IsEnabled();
1938 aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
1939 aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
1941 aImageRect.AdjustLeft( 1 );
1942 aImageRect.AdjustTop( 1 );
1943 aImageRect.AdjustRight( -1 );
1944 aImageRect.AdjustBottom( -1 );
1946 // display border and selection status
1947 aImageRect = aDecoView.DrawFrame(aImageRect, DrawFrameStyle::DoubleIn);
1948 if ((ImplGetButtonState() & DrawButtonFlags::Pressed) || !bEnabled)
1949 rRenderContext.SetFillColor( rStyleSettings.GetFaceColor());
1950 else
1951 rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
1952 rRenderContext.SetLineColor();
1953 rRenderContext.DrawRect(aImageRect);
1955 // display image
1956 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1957 if (!bEnabled)
1958 nImageStyle |= DrawImageFlags::Disable;
1960 Image* pImage = &maImage;
1962 Point aImagePos(aImageRect.TopLeft());
1963 aImagePos.AdjustX((aImageRect.GetWidth() - aImageSize.Width()) / 2 );
1964 aImagePos.AdjustY((aImageRect.GetHeight() - aImageSize.Height()) / 2 );
1965 if (IsZoom())
1966 rRenderContext.DrawImage(aImagePos, aImageSize, *pImage, nImageStyle);
1967 else
1968 rRenderContext.DrawImage(aImagePos, *pImage, nImageStyle);
1970 aImageRect.AdjustLeft( 1 );
1971 aImageRect.AdjustTop( 1 );
1972 aImageRect.AdjustRight( -1 );
1973 aImageRect.AdjustBottom( -1 );
1975 ImplSetFocusRect(aImageRect);
1977 if (mbChecked)
1979 rRenderContext.SetLineColor(rStyleSettings.GetHighlightColor());
1980 rRenderContext.SetFillColor();
1981 if ((aImageSize.Width() >= 20) || (aImageSize.Height() >= 20))
1983 aImageRect.AdjustLeft( 1 );
1984 aImageRect.AdjustTop( 1 );
1985 aImageRect.AdjustRight( -1 );
1986 aImageRect.AdjustBottom( -1 );
1988 rRenderContext.DrawRect(aImageRect);
1989 aImageRect.AdjustLeft( 1 );
1990 aImageRect.AdjustTop( 1 );
1991 aImageRect.AdjustRight( -1 );
1992 aImageRect.AdjustBottom( -1 );
1993 rRenderContext.DrawRect(aImageRect);
1996 if (HasFocus())
1997 ShowFocus(ImplGetFocusRect());
2002 void RadioButton::ImplDraw( OutputDevice* pDev, DrawFlags nDrawFlags,
2003 const Point& rPos, const Size& rSize,
2004 const Size& rImageSize, tools::Rectangle& rStateRect,
2005 tools::Rectangle& rMouseRect )
2007 WinBits nWinStyle = GetStyle();
2008 OUString aText( GetText() );
2010 pDev->Push( PushFlags::CLIPREGION );
2011 pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
2013 // no image radio button
2014 if ( !maImage )
2016 if (!aText.isEmpty() || HasImage())
2018 DrawTextFlags nTextStyle = Button::ImplGetTextStyle( nWinStyle, nDrawFlags );
2020 const long nImageSep = GetDrawPixel( pDev, ImplGetImageToTextDistance() );
2021 Size aSize( rSize );
2022 Point aPos( rPos );
2023 aPos.AdjustX(rImageSize.Width() + nImageSep );
2024 aSize.AdjustWidth( -(rImageSize.Width() + nImageSep) );
2026 // if the text rect height is smaller than the height of the image
2027 // then for single lines the default should be centered text
2028 if( (nWinStyle & (WB_TOP|WB_VCENTER|WB_BOTTOM)) == 0 &&
2029 (rImageSize.Height() > rSize.Height() || ! (nWinStyle & WB_WORDBREAK) ) )
2031 nTextStyle &= ~DrawTextFlags(DrawTextFlags::Top|DrawTextFlags::Bottom);
2032 nTextStyle |= DrawTextFlags::VCenter;
2033 aSize.setHeight( rImageSize.Height() );
2036 ImplDrawAlignedImage( pDev, aPos, aSize, 1, nTextStyle );
2038 rMouseRect = tools::Rectangle(aPos, aSize);
2039 rMouseRect.SetLeft(rPos.X());
2040 rMouseRect.SetTop(rPos.Y());
2042 rStateRect.SetLeft( rPos.X() );
2043 rStateRect.SetTop( rMouseRect.Top() );
2045 if ( aSize.Height() > rImageSize.Height() )
2046 rStateRect.AdjustTop(( aSize.Height() - rImageSize.Height() ) / 2 );
2047 else
2049 rStateRect.AdjustTop( -(( rImageSize.Height() - aSize.Height() ) / 2) );
2050 if( rStateRect.Top() < 0 )
2051 rStateRect.SetTop( 0 );
2054 rStateRect.SetRight( rStateRect.Left() + rImageSize.Width()-1 );
2055 rStateRect.SetBottom( rStateRect.Top() + rImageSize.Height()-1 );
2057 if ( rStateRect.Bottom() > rMouseRect.Bottom() )
2058 rMouseRect.SetBottom( rStateRect.Bottom() );
2060 else
2062 rStateRect.SetLeft( rPos.X() );
2063 if ( nWinStyle & WB_VCENTER )
2064 rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
2065 else if ( nWinStyle & WB_BOTTOM )
2066 rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() ); //-1;
2067 else
2068 rStateRect.SetTop( rPos.Y() );
2069 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
2070 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
2071 rMouseRect = rStateRect;
2073 ImplSetFocusRect( rStateRect );
2076 else
2078 bool bTopImage = (nWinStyle & WB_TOP) != 0;
2079 Size aImageSize = maImage.GetSizePixel();
2080 tools::Rectangle aImageRect( rPos, rSize );
2081 long nTextHeight = pDev->GetTextHeight();
2082 long nTextWidth = pDev->GetCtrlTextWidth( aText );
2084 // calculate position and sizes
2085 if (!aText.isEmpty())
2087 Size aTmpSize( (aImageSize.Width()+8), (aImageSize.Height()+8) );
2088 if ( bTopImage )
2090 aImageRect.SetLeft( (rSize.Width()-aTmpSize.Width())/2 );
2091 aImageRect.SetTop( (rSize.Height()-(aTmpSize.Height()+nTextHeight+6))/2 );
2093 else
2094 aImageRect.SetTop( (rSize.Height()-aTmpSize.Height())/2 );
2096 aImageRect.SetRight( aImageRect.Left()+aTmpSize.Width() );
2097 aImageRect.SetBottom( aImageRect.Top()+aTmpSize.Height() );
2099 // display text
2100 Point aTxtPos = rPos;
2101 if ( bTopImage )
2103 aTxtPos.AdjustX((rSize.Width()-nTextWidth)/2 );
2104 aTxtPos.AdjustY(aImageRect.Bottom()+6 );
2106 else
2108 aTxtPos.AdjustX(aImageRect.Right()+8 );
2109 aTxtPos.AdjustY((rSize.Height()-nTextHeight)/2 );
2111 pDev->DrawCtrlText( aTxtPos, aText, 0, aText.getLength() );
2114 rMouseRect = aImageRect;
2115 rStateRect = aImageRect;
2118 pDev->Pop();
2121 void RadioButton::ImplDrawRadioButton(vcl::RenderContext& rRenderContext)
2123 HideFocus();
2125 Size aImageSize;
2126 if (!maImage)
2127 aImageSize = ImplGetRadioImageSize();
2128 else
2129 aImageSize = maImage.GetSizePixel();
2131 aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
2132 aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
2134 // Draw control text
2135 ImplDraw(&rRenderContext, DrawFlags::NONE, Point(), GetOutputSizePixel(),
2136 aImageSize, maStateRect, maMouseRect);
2138 if (!maImage && HasFocus())
2139 ShowFocus(ImplGetFocusRect());
2141 ImplDrawRadioButtonState(rRenderContext);
2144 void RadioButton::group(RadioButton &rOther)
2146 if (&rOther == this)
2147 return;
2149 if (!m_xGroup)
2151 m_xGroup.reset(new std::vector<VclPtr<RadioButton> >);
2152 m_xGroup->push_back(this);
2155 auto aFind = std::find(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(&rOther));
2156 if (aFind == m_xGroup->end())
2158 m_xGroup->push_back(&rOther);
2160 if (rOther.m_xGroup)
2162 std::vector< VclPtr<RadioButton> > aOthers(rOther.GetRadioButtonGroup(false));
2163 //make all members of the group share the same button group
2164 for (auto const& elem : aOthers)
2166 aFind = std::find(m_xGroup->begin(), m_xGroup->end(), elem);
2167 if (aFind == m_xGroup->end())
2168 m_xGroup->push_back(elem);
2172 //make all members of the group share the same button group
2173 for (VclPtr<RadioButton> const & pButton : *m_xGroup)
2175 pButton->m_xGroup = m_xGroup;
2179 //if this one is checked, uncheck all the others
2180 if (mbChecked)
2181 ImplUncheckAllOther();
2184 std::vector< VclPtr<RadioButton> > RadioButton::GetRadioButtonGroup(bool bIncludeThis) const
2186 if (m_xGroup)
2188 if (bIncludeThis)
2189 return *m_xGroup;
2190 std::vector< VclPtr<RadioButton> > aGroup;
2191 for (VclPtr<RadioButton> const & pRadioButton : *m_xGroup)
2193 if (pRadioButton == this)
2194 continue;
2195 aGroup.push_back(pRadioButton);
2197 return aGroup;
2200 //old-school
2202 // go back to first in group;
2203 vcl::Window* pFirst = const_cast<RadioButton*>(this);
2204 while( ( pFirst->GetStyle() & WB_GROUP ) == 0 )
2206 vcl::Window* pWindow = pFirst->GetWindow( GetWindowType::Prev );
2207 if( pWindow )
2208 pFirst = pWindow;
2209 else
2210 break;
2212 std::vector< VclPtr<RadioButton> > aGroup;
2213 // insert radiobuttons up to next group
2216 if( pFirst->GetType() == WindowType::RADIOBUTTON )
2218 if( pFirst != this || bIncludeThis )
2219 aGroup.emplace_back(static_cast<RadioButton*>(pFirst) );
2221 pFirst = pFirst->GetWindow( GetWindowType::Next );
2222 } while( pFirst && ( ( pFirst->GetStyle() & WB_GROUP ) == 0 ) );
2224 return aGroup;
2227 void RadioButton::ImplUncheckAllOther( const bool bSetStyle )
2229 if ( bSetStyle )
2230 mpWindowImpl->mnStyle |= WB_TABSTOP;
2232 std::vector<VclPtr<RadioButton> > aGroup(GetRadioButtonGroup(false));
2233 // iterate over radio button group and checked buttons
2234 for (VclPtr<RadioButton>& pWindow : aGroup)
2236 if ( pWindow->IsChecked() )
2238 pWindow->SetState( false );
2239 if ( pWindow->IsDisposed() )
2240 return;
2243 // not inside if clause to always remove wrongly set WB_TABSTOPS
2244 pWindow->mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2248 void RadioButton::ImplCallClick( bool bGrabFocus, GetFocusFlags nFocusFlags )
2250 mbStateChanged = !mbChecked;
2251 mbChecked = true;
2252 mpWindowImpl->mnStyle |= WB_TABSTOP;
2253 Invalidate();
2254 Update();
2255 VclPtr<vcl::Window> xWindow = this;
2256 if ( mbRadioCheck )
2257 ImplUncheckAllOther();
2258 if ( xWindow->IsDisposed() )
2259 return;
2260 if ( bGrabFocus )
2261 ImplGrabFocus( nFocusFlags );
2262 if ( xWindow->IsDisposed() )
2263 return;
2264 if ( mbStateChanged )
2265 Toggle();
2266 if ( xWindow->IsDisposed() )
2267 return;
2268 Click();
2269 if ( xWindow->IsDisposed() )
2270 return;
2271 mbStateChanged = false;
2274 RadioButton::RadioButton( vcl::Window* pParent, WinBits nStyle ) :
2275 Button( WindowType::RADIOBUTTON )
2277 ImplInitRadioButtonData();
2278 ImplInit( pParent, nStyle );
2281 RadioButton::~RadioButton()
2283 disposeOnce();
2286 void RadioButton::dispose()
2288 if (m_xGroup)
2290 m_xGroup->erase(std::remove(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(this)),
2291 m_xGroup->end());
2292 m_xGroup.reset();
2294 Button::dispose();
2297 void RadioButton::MouseButtonDown( const MouseEvent& rMEvt )
2299 if ( rMEvt.IsLeft() && maMouseRect.IsInside( rMEvt.GetPosPixel() ) )
2301 ImplGetButtonState() |= DrawButtonFlags::Pressed;
2302 Invalidate();
2303 Update();
2304 StartTracking();
2305 return;
2308 Button::MouseButtonDown( rMEvt );
2311 void RadioButton::Tracking( const TrackingEvent& rTEvt )
2313 if ( rTEvt.IsTrackingEnded() )
2315 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
2317 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
2318 GrabFocus();
2320 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
2322 // do not call click handler if aborted
2323 if ( !rTEvt.IsTrackingCanceled() )
2324 ImplCallClick();
2325 else
2327 Invalidate();
2328 Update();
2332 else
2334 if ( maMouseRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() ) )
2336 if ( !(ImplGetButtonState() & DrawButtonFlags::Pressed) )
2338 ImplGetButtonState() |= DrawButtonFlags::Pressed;
2339 Invalidate();
2340 Update();
2343 else
2345 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
2347 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
2348 Invalidate();
2349 Update();
2355 void RadioButton::KeyInput( const KeyEvent& rKEvt )
2357 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
2359 if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
2361 if ( !(ImplGetButtonState() & DrawButtonFlags::Pressed) )
2363 ImplGetButtonState() |= DrawButtonFlags::Pressed;
2364 Invalidate();
2365 Update();
2368 else if ( (ImplGetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
2370 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
2371 Invalidate();
2372 Update();
2374 else
2375 Button::KeyInput( rKEvt );
2378 void RadioButton::KeyUp( const KeyEvent& rKEvt )
2380 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
2382 if ( (ImplGetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
2384 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
2385 ImplCallClick();
2387 else
2388 Button::KeyUp( rKEvt );
2391 void RadioButton::FillLayoutData() const
2393 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
2394 const_cast<RadioButton*>(this)->Invalidate();
2397 void RadioButton::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
2399 ImplDrawRadioButton(rRenderContext);
2402 void RadioButton::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize,
2403 DrawFlags nFlags )
2405 if ( !maImage )
2407 MapMode aResMapMode( MapUnit::Map100thMM );
2408 Point aPos = pDev->LogicToPixel( rPos );
2409 Size aSize = pDev->LogicToPixel( rSize );
2410 Size aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
2411 Size aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
2412 Size aBrd2Size = pDev->LogicToPixel( Size( 60, 60 ), aResMapMode );
2413 vcl::Font aFont = GetDrawPixelFont( pDev );
2414 tools::Rectangle aStateRect;
2415 tools::Rectangle aMouseRect;
2417 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
2418 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
2419 aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
2420 aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
2421 aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
2422 aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
2424 if ( !aBrd1Size.Width() )
2425 aBrd1Size.setWidth( 1 );
2426 if ( !aBrd1Size.Height() )
2427 aBrd1Size.setHeight( 1 );
2428 if ( !aBrd2Size.Width() )
2429 aBrd2Size.setWidth( 1 );
2430 if ( !aBrd2Size.Height() )
2431 aBrd2Size.setHeight( 1 );
2433 pDev->Push();
2434 pDev->SetMapMode();
2435 pDev->SetFont( aFont );
2436 if ( nFlags & DrawFlags::Mono )
2437 pDev->SetTextColor( COL_BLACK );
2438 else
2439 pDev->SetTextColor( GetTextColor() );
2440 pDev->SetTextFillColor();
2442 ImplDraw( pDev, nFlags, aPos, aSize,
2443 aImageSize, aStateRect, aMouseRect );
2445 Point aCenterPos = aStateRect.Center();
2446 long nRadX = aImageSize.Width()/2;
2447 long nRadY = aImageSize.Height()/2;
2449 pDev->SetLineColor();
2450 pDev->SetFillColor( COL_BLACK );
2451 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2452 nRadX -= aBrd1Size.Width();
2453 nRadY -= aBrd1Size.Height();
2454 pDev->SetFillColor( COL_WHITE );
2455 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2456 if ( mbChecked )
2458 nRadX -= aBrd1Size.Width();
2459 nRadY -= aBrd1Size.Height();
2460 if ( !nRadX )
2461 nRadX = 1;
2462 if ( !nRadY )
2463 nRadY = 1;
2464 pDev->SetFillColor( COL_BLACK );
2465 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2468 pDev->Pop();
2470 else
2472 OSL_FAIL( "RadioButton::Draw() - not implemented for RadioButton with Image" );
2476 void RadioButton::Resize()
2478 Control::Resize();
2479 Invalidate();
2482 void RadioButton::GetFocus()
2484 ShowFocus( ImplGetFocusRect() );
2485 SetInputContext( InputContext( GetFont() ) );
2486 Button::GetFocus();
2489 void RadioButton::LoseFocus()
2491 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
2493 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
2494 Invalidate();
2495 Update();
2498 HideFocus();
2499 Button::LoseFocus();
2502 void RadioButton::StateChanged( StateChangedType nType )
2504 Button::StateChanged( nType );
2506 if ( nType == StateChangedType::State )
2508 if ( IsReallyVisible() && IsUpdateMode() )
2509 Invalidate( maStateRect );
2511 else if ( (nType == StateChangedType::Enable) ||
2512 (nType == StateChangedType::Text) ||
2513 (nType == StateChangedType::Data) ||
2514 (nType == StateChangedType::UpdateMode) )
2516 if ( IsUpdateMode() )
2517 Invalidate();
2519 else if ( nType == StateChangedType::Style )
2521 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
2523 if ( (GetPrevStyle() & RADIOBUTTON_VIEW_STYLE) !=
2524 (GetStyle() & RADIOBUTTON_VIEW_STYLE) )
2526 if ( IsUpdateMode() )
2527 Invalidate();
2530 else if ( (nType == StateChangedType::Zoom) ||
2531 (nType == StateChangedType::ControlFont) )
2533 ImplInitSettings( false );
2534 Invalidate();
2536 else if ( nType == StateChangedType::ControlForeground )
2538 ImplInitSettings( false );
2539 Invalidate();
2541 else if ( nType == StateChangedType::ControlBackground )
2543 ImplInitSettings( true );
2544 Invalidate();
2548 void RadioButton::DataChanged( const DataChangedEvent& rDCEvt )
2550 Button::DataChanged( rDCEvt );
2552 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2553 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2554 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2555 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2557 ImplInitSettings( true );
2558 Invalidate();
2562 bool RadioButton::PreNotify( NotifyEvent& rNEvt )
2564 const MouseEvent* pMouseEvt = nullptr;
2566 if( (rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != nullptr )
2568 if( !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
2570 // trigger redraw if mouse over state has changed
2571 if( IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire) )
2573 if (maMouseRect.IsInside(GetPointerPosPixel()) != maMouseRect.IsInside(GetLastPointerPosPixel()) ||
2574 pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
2576 Invalidate( maStateRect );
2582 return Button::PreNotify(rNEvt);
2585 void RadioButton::Toggle()
2587 ImplCallEventListenersAndHandler( VclEventId::RadiobuttonToggle, [this] () { maToggleHdl.Call(*this); } );
2590 void RadioButton::SetModeRadioImage( const Image& rImage )
2592 if ( rImage != maImage )
2594 maImage = rImage;
2595 CompatStateChanged( StateChangedType::Data );
2596 queue_resize();
2601 void RadioButton::SetState( bool bCheck )
2603 // carry the TabStop flag along correctly
2604 if ( bCheck )
2605 mpWindowImpl->mnStyle |= WB_TABSTOP;
2606 else
2607 mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2609 if ( mbChecked != bCheck )
2611 mbChecked = bCheck;
2612 CompatStateChanged( StateChangedType::State );
2613 Toggle();
2617 bool RadioButton::set_property(const OString &rKey, const OUString &rValue)
2619 if (rKey == "active")
2620 SetState(toBool(rValue));
2621 else if (rKey == "image-position")
2623 WinBits nBits = GetStyle();
2624 if (rValue == "left")
2626 nBits &= ~(WB_CENTER | WB_RIGHT);
2627 nBits |= WB_LEFT;
2629 else if (rValue == "right")
2631 nBits &= ~(WB_CENTER | WB_LEFT);
2632 nBits |= WB_RIGHT;
2634 else if (rValue == "top")
2636 nBits &= ~(WB_VCENTER | WB_BOTTOM);
2637 nBits |= WB_TOP;
2639 else if (rValue == "bottom")
2641 nBits &= ~(WB_VCENTER | WB_TOP);
2642 nBits |= WB_BOTTOM;
2644 //It's rather mad to have to set these bits when there is the other
2645 //image align. Looks like e.g. the radiobuttons etc weren't converted
2646 //over to image align fully.
2647 SetStyle(nBits);
2648 //Deliberate to set the sane ImageAlign property
2649 return Button::set_property(rKey, rValue);
2651 else
2652 return Button::set_property(rKey, rValue);
2653 return true;
2656 void RadioButton::Check( bool bCheck )
2658 // TabStop-Flag richtig mitfuehren
2659 if ( bCheck )
2660 mpWindowImpl->mnStyle |= WB_TABSTOP;
2661 else
2662 mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2664 if ( mbChecked != bCheck )
2666 mbChecked = bCheck;
2667 VclPtr<vcl::Window> xWindow = this;
2668 CompatStateChanged( StateChangedType::State );
2669 if ( xWindow->IsDisposed() )
2670 return;
2671 if ( bCheck && mbRadioCheck )
2672 ImplUncheckAllOther();
2673 if ( xWindow->IsDisposed() )
2674 return;
2675 Toggle();
2679 long RadioButton::ImplGetImageToTextDistance() const
2681 // 4 pixels, but take zoom into account, so the text doesn't "jump" relative to surrounding elements,
2682 // which might have been aligned with the text of the check box
2683 return CalcZoom( 4 );
2686 Size RadioButton::ImplGetRadioImageSize() const
2688 Size aSize;
2689 bool bDefaultSize = true;
2690 if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
2692 ImplControlValue aControlValue;
2693 tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
2694 tools::Rectangle aBoundingRgn, aContentRgn;
2696 // get native size of a radio button
2697 if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
2698 ControlState::DEFAULT|ControlState::ENABLED,
2699 aControlValue,
2700 aBoundingRgn, aContentRgn ) )
2702 aSize = aContentRgn.GetSize();
2703 bDefaultSize = false;
2706 if( bDefaultSize )
2707 aSize = GetRadioImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
2708 return aSize;
2711 static void LoadThemedImageList(const StyleSettings &rStyleSettings,
2712 std::vector<Image>& rList, const std::vector<OUString> &rResources)
2714 Color aColorAry1[6];
2715 Color aColorAry2[6];
2716 aColorAry1[0] = Color( 0xC0, 0xC0, 0xC0 );
2717 aColorAry1[1] = Color( 0xFF, 0xFF, 0x00 );
2718 aColorAry1[2] = Color( 0xFF, 0xFF, 0xFF );
2719 aColorAry1[3] = Color( 0x80, 0x80, 0x80 );
2720 aColorAry1[4] = Color( 0x00, 0x00, 0x00 );
2721 aColorAry1[5] = Color( 0x00, 0xFF, 0x00 );
2722 aColorAry2[0] = rStyleSettings.GetFaceColor();
2723 aColorAry2[1] = rStyleSettings.GetWindowColor();
2724 aColorAry2[2] = rStyleSettings.GetLightColor();
2725 aColorAry2[3] = rStyleSettings.GetShadowColor();
2726 aColorAry2[4] = rStyleSettings.GetDarkShadowColor();
2727 aColorAry2[5] = rStyleSettings.GetWindowTextColor();
2729 static_assert( sizeof(aColorAry1) == sizeof(aColorAry2), "aColorAry1 must match aColorAry2" );
2731 for (const auto &a : rResources)
2733 BitmapEx aBmpEx(a);
2734 aBmpEx.Replace(aColorAry1, aColorAry2, SAL_N_ELEMENTS(aColorAry1));
2735 rList.emplace_back(aBmpEx);
2739 Image RadioButton::GetRadioImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
2741 ImplSVData* pSVData = ImplGetSVData();
2742 const StyleSettings& rStyleSettings = rSettings.GetStyleSettings();
2743 sal_uInt16 nStyle = 0;
2745 if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
2746 nStyle = STYLE_RADIOBUTTON_MONO;
2748 if ( pSVData->maCtrlData.maRadioImgList.empty() ||
2749 (pSVData->maCtrlData.mnRadioStyle != nStyle) ||
2750 (pSVData->maCtrlData.mnLastRadioFColor != rStyleSettings.GetFaceColor()) ||
2751 (pSVData->maCtrlData.mnLastRadioWColor != rStyleSettings.GetWindowColor()) ||
2752 (pSVData->maCtrlData.mnLastRadioLColor != rStyleSettings.GetLightColor()) )
2754 pSVData->maCtrlData.maRadioImgList.clear();
2756 pSVData->maCtrlData.mnLastRadioFColor = rStyleSettings.GetFaceColor();
2757 pSVData->maCtrlData.mnLastRadioWColor = rStyleSettings.GetWindowColor();
2758 pSVData->maCtrlData.mnLastRadioLColor = rStyleSettings.GetLightColor();
2760 std::vector<OUString> aResources;
2761 if (nStyle)
2763 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO1);
2764 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO2);
2765 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO3);
2766 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO4);
2767 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO5);
2768 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO6);
2770 else
2772 aResources.emplace_back(SV_RESID_BITMAP_RADIO1);
2773 aResources.emplace_back(SV_RESID_BITMAP_RADIO2);
2774 aResources.emplace_back(SV_RESID_BITMAP_RADIO3);
2775 aResources.emplace_back(SV_RESID_BITMAP_RADIO4);
2776 aResources.emplace_back(SV_RESID_BITMAP_RADIO5);
2777 aResources.emplace_back(SV_RESID_BITMAP_RADIO6);
2779 LoadThemedImageList( rStyleSettings, pSVData->maCtrlData.maRadioImgList, aResources);
2780 pSVData->maCtrlData.mnRadioStyle = nStyle;
2783 sal_uInt16 nIndex;
2784 if ( nFlags & DrawButtonFlags::Disabled )
2786 if ( nFlags & DrawButtonFlags::Checked )
2787 nIndex = 5;
2788 else
2789 nIndex = 4;
2791 else if ( nFlags & DrawButtonFlags::Pressed )
2793 if ( nFlags & DrawButtonFlags::Checked )
2794 nIndex = 3;
2795 else
2796 nIndex = 2;
2798 else
2800 if ( nFlags & DrawButtonFlags::Checked )
2801 nIndex = 1;
2802 else
2803 nIndex = 0;
2805 return pSVData->maCtrlData.maRadioImgList[nIndex];
2808 void RadioButton::ImplSetMinimumNWFSize()
2810 Push( PushFlags::MAPMODE );
2811 SetMapMode(MapMode(MapUnit::MapPixel));
2813 ImplControlValue aControlValue;
2814 Size aCurSize( GetSizePixel() );
2815 tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
2816 tools::Rectangle aBoundingRgn, aContentRgn;
2818 // get native size of a radiobutton
2819 if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
2820 ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
2821 aBoundingRgn, aContentRgn ) )
2823 Size aSize = aContentRgn.GetSize();
2825 if( aSize.Height() > aCurSize.Height() )
2827 aCurSize.setHeight( aSize.Height() );
2828 SetSizePixel( aCurSize );
2832 Pop();
2835 Size RadioButton::CalcMinimumSize() const
2837 Size aSize;
2838 if ( !maImage )
2839 aSize = ImplGetRadioImageSize();
2840 else
2842 aSize = maImage.GetSizePixel();
2843 aSize.AdjustWidth(8);
2844 aSize.AdjustHeight(8);
2847 if (Button::HasImage())
2849 Size aImgSize = GetModeImage().GetSizePixel();
2850 aSize = Size(std::max(aImgSize.Width(), aSize.Width()),
2851 std::max(aImgSize.Height(), aSize.Height()));
2854 OUString aText = GetText();
2855 if (!aText.isEmpty())
2857 bool bTopImage = (GetStyle() & WB_TOP) != 0;
2859 Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( 0x7fffffff, 0x7fffffff ) ),
2860 aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
2862 aSize.AdjustWidth(2 ); // for focus rect
2864 if (!bTopImage)
2866 aSize.AdjustWidth(ImplGetImageToTextDistance() );
2867 aSize.AdjustWidth(aTextSize.Width() );
2868 if ( aSize.Height() < aTextSize.Height() )
2869 aSize.setHeight( aTextSize.Height() );
2871 else
2873 aSize.AdjustHeight(6 );
2874 aSize.AdjustHeight(GetTextHeight() );
2875 if ( aSize.Width() < aTextSize.Width() )
2876 aSize.setWidth( aTextSize.Width() );
2880 return CalcWindowSize( aSize );
2883 Size RadioButton::GetOptimalSize() const
2885 return CalcMinimumSize();
2888 void RadioButton::ShowFocus(const tools::Rectangle& rRect)
2890 if (IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Focus))
2892 ImplControlValue aControlValue;
2893 tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
2895 aInRect.SetLeft( rRect.Left() ); // exclude the radio element itself from the focusrect
2897 DrawNativeControl(ControlType::Radiobutton, ControlPart::Focus, aInRect,
2898 ControlState::FOCUSED, aControlValue, OUString());
2900 Button::ShowFocus(rRect);
2903 FactoryFunction RadioButton::GetUITestFactory() const
2905 return RadioButtonUIObject::create;
2908 void CheckBox::ImplInitCheckBoxData()
2910 meState = TRISTATE_FALSE;
2911 meSaveValue = TRISTATE_FALSE;
2912 mbTriState = false;
2915 void CheckBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
2917 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
2918 Button::ImplInit( pParent, nStyle, nullptr );
2920 ImplInitSettings( true );
2923 WinBits CheckBox::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
2925 if ( !(nStyle & WB_NOTABSTOP) )
2926 nStyle |= WB_TABSTOP;
2927 if ( !(nStyle & WB_NOGROUP) &&
2928 (!pPrevWindow || (pPrevWindow->GetType() != WindowType::CHECKBOX)) )
2929 nStyle |= WB_GROUP;
2930 return nStyle;
2933 const vcl::Font& CheckBox::GetCanonicalFont( const StyleSettings& _rStyle ) const
2935 return _rStyle.GetRadioCheckFont();
2938 const Color& CheckBox::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
2940 return _rStyle.GetRadioCheckTextColor();
2943 void CheckBox::ImplInitSettings( bool bBackground )
2945 Button::ImplInitSettings();
2947 if ( bBackground )
2949 vcl::Window* pParent = GetParent();
2950 if ( !IsControlBackground() &&
2951 (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) ) )
2953 EnableChildTransparentMode();
2954 SetParentClipMode( ParentClipMode::NoClip );
2955 SetPaintTransparent( true );
2956 SetBackground();
2957 if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
2958 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
2960 else
2962 EnableChildTransparentMode( false );
2963 SetParentClipMode();
2964 SetPaintTransparent( false );
2966 if ( IsControlBackground() )
2967 SetBackground( GetControlBackground() );
2968 else
2969 SetBackground( pParent->GetBackground() );
2974 void CheckBox::ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext)
2976 bool bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire);
2977 if (bNativeOK)
2979 ImplControlValue aControlValue(meState == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
2980 tools::Rectangle aCtrlRegion(maStateRect);
2981 ControlState nState = ControlState::NONE;
2983 if (HasFocus())
2984 nState |= ControlState::FOCUSED;
2985 if (ImplGetButtonState() & DrawButtonFlags::Default)
2986 nState |= ControlState::DEFAULT;
2987 if (ImplGetButtonState() & DrawButtonFlags::Pressed)
2988 nState |= ControlState::PRESSED;
2989 if (IsEnabled())
2990 nState |= ControlState::ENABLED;
2992 if (meState == TRISTATE_TRUE)
2993 aControlValue.setTristateVal(ButtonValue::On);
2994 else if (meState == TRISTATE_INDET)
2995 aControlValue.setTristateVal(ButtonValue::Mixed);
2997 if (IsMouseOver() && maMouseRect.IsInside(GetPointerPosPixel()))
2998 nState |= ControlState::ROLLOVER;
3000 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3001 nState, aControlValue, OUString());
3004 if (!bNativeOK)
3006 DrawButtonFlags nStyle = ImplGetButtonState();
3007 if (!IsEnabled())
3008 nStyle |= DrawButtonFlags::Disabled;
3009 if (meState == TRISTATE_INDET)
3010 nStyle |= DrawButtonFlags::DontKnow;
3011 else if (meState == TRISTATE_TRUE)
3012 nStyle |= DrawButtonFlags::Checked;
3013 Image aImage = GetCheckImage(GetSettings(), nStyle);
3014 if (IsZoom())
3015 rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
3016 else
3017 rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
3021 void CheckBox::ImplDraw( OutputDevice* pDev, DrawFlags nDrawFlags,
3022 const Point& rPos, const Size& rSize,
3023 const Size& rImageSize, tools::Rectangle& rStateRect,
3024 tools::Rectangle& rMouseRect )
3026 WinBits nWinStyle = GetStyle();
3027 OUString aText( GetText() );
3029 pDev->Push( PushFlags::CLIPREGION | PushFlags::LINECOLOR );
3030 pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
3032 if (!aText.isEmpty() || HasImage())
3034 DrawTextFlags nTextStyle = Button::ImplGetTextStyle( nWinStyle, nDrawFlags );
3036 const long nImageSep = GetDrawPixel( pDev, ImplGetImageToTextDistance() );
3037 Size aSize( rSize );
3038 Point aPos( rPos );
3039 aPos.AdjustX(rImageSize.Width() + nImageSep );
3040 aSize.AdjustWidth( -(rImageSize.Width() + nImageSep) );
3042 // if the text rect height is smaller than the height of the image
3043 // then for single lines the default should be centered text
3044 if( (nWinStyle & (WB_TOP|WB_VCENTER|WB_BOTTOM)) == 0 &&
3045 (rImageSize.Height() > rSize.Height() || ! (nWinStyle & WB_WORDBREAK) ) )
3047 nTextStyle &= ~DrawTextFlags(DrawTextFlags::Top|DrawTextFlags::Bottom);
3048 nTextStyle |= DrawTextFlags::VCenter;
3049 aSize.setHeight( rImageSize.Height() );
3052 ImplDrawAlignedImage( pDev, aPos, aSize, 1, nTextStyle );
3054 rMouseRect = tools::Rectangle( aPos, aSize );
3055 rMouseRect.SetLeft( rPos.X() );
3056 rStateRect.SetLeft( rPos.X() );
3057 rStateRect.SetTop( rMouseRect.Top() );
3059 if ( aSize.Height() > rImageSize.Height() )
3060 rStateRect.AdjustTop(( aSize.Height() - rImageSize.Height() ) / 2 );
3061 else
3063 rStateRect.AdjustTop( -(( rImageSize.Height() - aSize.Height() ) / 2) );
3064 if( rStateRect.Top() < 0 )
3065 rStateRect.SetTop( 0 );
3068 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
3069 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
3070 if ( rStateRect.Bottom() > rMouseRect.Bottom() )
3071 rMouseRect.SetBottom( rStateRect.Bottom() );
3073 else
3075 if ( mbLegacyNoTextAlign && ( nWinStyle & WB_CENTER ) )
3076 rStateRect.SetLeft( rPos.X()+((rSize.Width()-rImageSize.Width())/2) );
3077 else if ( mbLegacyNoTextAlign && ( nWinStyle & WB_RIGHT ) )
3078 rStateRect.SetLeft( rPos.X()+rSize.Width()-rImageSize.Width() );
3079 else
3080 rStateRect.SetLeft( rPos.X() );
3081 if ( nWinStyle & WB_VCENTER )
3082 rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
3083 else if ( nWinStyle & WB_BOTTOM )
3084 rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() );
3085 else
3086 rStateRect.SetTop( rPos.Y() );
3087 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
3088 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
3089 // provide space for focusrect
3090 // note: this assumes that the control's size was adjusted
3091 // accordingly in Get/LoseFocus, so the onscreen position won't change
3092 if( HasFocus() )
3093 rStateRect.Move( 1, 1 );
3094 rMouseRect = rStateRect;
3096 ImplSetFocusRect( rStateRect );
3099 pDev->Pop();
3102 void CheckBox::ImplDrawCheckBox(vcl::RenderContext& rRenderContext)
3104 Size aImageSize = ImplGetCheckImageSize();
3105 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
3106 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
3108 HideFocus();
3110 ImplDraw(&rRenderContext, DrawFlags::NONE, Point(), GetOutputSizePixel(),
3111 aImageSize, maStateRect, maMouseRect);
3113 ImplDrawCheckBoxState(rRenderContext);
3114 if (HasFocus())
3115 ShowFocus(ImplGetFocusRect());
3118 void CheckBox::ImplCheck()
3120 TriState eNewState;
3121 if ( meState == TRISTATE_FALSE )
3122 eNewState = TRISTATE_TRUE;
3123 else if ( !mbTriState )
3124 eNewState = TRISTATE_FALSE;
3125 else if ( meState == TRISTATE_TRUE )
3126 eNewState = TRISTATE_INDET;
3127 else
3128 eNewState = TRISTATE_FALSE;
3129 meState = eNewState;
3131 VclPtr<vcl::Window> xWindow = this;
3132 Invalidate();
3133 Update();
3134 Toggle();
3135 if ( xWindow->IsDisposed() )
3136 return;
3137 Click();
3140 CheckBox::CheckBox( vcl::Window* pParent, WinBits nStyle ) :
3141 Button( WindowType::CHECKBOX ), mbLegacyNoTextAlign( false )
3143 ImplInitCheckBoxData();
3144 ImplInit( pParent, nStyle );
3147 void CheckBox::MouseButtonDown( const MouseEvent& rMEvt )
3149 if ( rMEvt.IsLeft() && maMouseRect.IsInside( rMEvt.GetPosPixel() ) )
3151 ImplGetButtonState() |= DrawButtonFlags::Pressed;
3152 Invalidate();
3153 Update();
3154 StartTracking();
3155 return;
3158 Button::MouseButtonDown( rMEvt );
3161 void CheckBox::Tracking( const TrackingEvent& rTEvt )
3163 if ( rTEvt.IsTrackingEnded() )
3165 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
3167 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
3168 GrabFocus();
3170 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
3172 // do not call click handler if aborted
3173 if ( !rTEvt.IsTrackingCanceled() )
3174 ImplCheck();
3175 else
3177 Invalidate();
3178 Update();
3182 else
3184 if ( maMouseRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() ) )
3186 if ( !(ImplGetButtonState() & DrawButtonFlags::Pressed) )
3188 ImplGetButtonState() |= DrawButtonFlags::Pressed;
3189 Invalidate();
3190 Update();
3193 else
3195 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
3197 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
3198 Invalidate();
3199 Update();
3205 void CheckBox::KeyInput( const KeyEvent& rKEvt )
3207 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3209 if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
3211 if ( !(ImplGetButtonState() & DrawButtonFlags::Pressed) )
3213 ImplGetButtonState() |= DrawButtonFlags::Pressed;
3214 Invalidate();
3215 Update();
3218 else if ( (ImplGetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
3220 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
3221 Invalidate();
3222 Update();
3224 else
3225 Button::KeyInput( rKEvt );
3228 void CheckBox::KeyUp( const KeyEvent& rKEvt )
3230 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3232 if ( (ImplGetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
3234 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
3235 ImplCheck();
3237 else
3238 Button::KeyUp( rKEvt );
3241 void CheckBox::FillLayoutData() const
3243 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
3244 const_cast<CheckBox*>(this)->Invalidate();
3247 void CheckBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
3249 ImplDrawCheckBox(rRenderContext);
3252 void CheckBox::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize,
3253 DrawFlags nFlags )
3255 MapMode aResMapMode( MapUnit::Map100thMM );
3256 Point aPos = pDev->LogicToPixel( rPos );
3257 Size aSize = pDev->LogicToPixel( rSize );
3258 Size aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
3259 Size aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
3260 Size aBrd2Size = pDev->LogicToPixel( Size( 30, 30 ), aResMapMode );
3261 long nCheckWidth = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode ).Width();
3262 vcl::Font aFont = GetDrawPixelFont( pDev );
3263 tools::Rectangle aStateRect;
3264 tools::Rectangle aMouseRect;
3266 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
3267 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
3268 aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
3269 aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
3270 aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
3271 aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
3273 if ( !aBrd1Size.Width() )
3274 aBrd1Size.setWidth( 1 );
3275 if ( !aBrd1Size.Height() )
3276 aBrd1Size.setHeight( 1 );
3277 if ( !aBrd2Size.Width() )
3278 aBrd2Size.setWidth( 1 );
3279 if ( !aBrd2Size.Height() )
3280 aBrd2Size.setHeight( 1 );
3281 if ( !nCheckWidth )
3282 nCheckWidth = 1;
3284 pDev->Push();
3285 pDev->SetMapMode();
3286 pDev->SetFont( aFont );
3287 if ( nFlags & DrawFlags::Mono )
3288 pDev->SetTextColor( COL_BLACK );
3289 else
3290 pDev->SetTextColor( GetTextColor() );
3291 pDev->SetTextFillColor();
3293 ImplDraw( pDev, nFlags, aPos, aSize,
3294 aImageSize, aStateRect, aMouseRect );
3296 pDev->SetLineColor();
3297 pDev->SetFillColor( COL_BLACK );
3298 pDev->DrawRect( aStateRect );
3299 aStateRect.AdjustLeft(aBrd1Size.Width() );
3300 aStateRect.AdjustTop(aBrd1Size.Height() );
3301 aStateRect.AdjustRight( -(aBrd1Size.Width()) );
3302 aStateRect.AdjustBottom( -(aBrd1Size.Height()) );
3303 if ( meState == TRISTATE_INDET )
3304 pDev->SetFillColor( COL_LIGHTGRAY );
3305 else
3306 pDev->SetFillColor( COL_WHITE );
3307 pDev->DrawRect( aStateRect );
3309 if ( meState == TRISTATE_TRUE )
3311 aStateRect.AdjustLeft(aBrd2Size.Width() );
3312 aStateRect.AdjustTop(aBrd2Size.Height() );
3313 aStateRect.AdjustRight( -(aBrd2Size.Width()) );
3314 aStateRect.AdjustBottom( -(aBrd2Size.Height()) );
3315 Point aPos11( aStateRect.TopLeft() );
3316 Point aPos12( aStateRect.BottomRight() );
3317 Point aPos21( aStateRect.TopRight() );
3318 Point aPos22( aStateRect.BottomLeft() );
3319 Point aTempPos11( aPos11 );
3320 Point aTempPos12( aPos12 );
3321 Point aTempPos21( aPos21 );
3322 Point aTempPos22( aPos22 );
3323 pDev->SetLineColor( COL_BLACK );
3324 long nDX = 0;
3325 for ( long i = 0; i < nCheckWidth; i++ )
3327 if ( !(i % 2) )
3329 aTempPos11.setX( aPos11.X()+nDX );
3330 aTempPos12.setX( aPos12.X()+nDX );
3331 aTempPos21.setX( aPos21.X()+nDX );
3332 aTempPos22.setX( aPos22.X()+nDX );
3334 else
3336 nDX++;
3337 aTempPos11.setX( aPos11.X()-nDX );
3338 aTempPos12.setX( aPos12.X()-nDX );
3339 aTempPos21.setX( aPos21.X()-nDX );
3340 aTempPos22.setX( aPos22.X()-nDX );
3342 pDev->DrawLine( aTempPos11, aTempPos12 );
3343 pDev->DrawLine( aTempPos21, aTempPos22 );
3347 pDev->Pop();
3350 void CheckBox::Resize()
3352 Control::Resize();
3353 Invalidate();
3356 void CheckBox::GetFocus()
3358 if (GetText().isEmpty())
3360 // increase button size to have space for focus rect
3361 // checkboxes without text will draw focusrect around the check
3362 // See CheckBox::ImplDraw()
3363 Point aPos( GetPosPixel() );
3364 Size aSize( GetSizePixel() );
3365 aPos.Move(-1,-1);
3366 aSize.AdjustHeight(2 );
3367 aSize.AdjustWidth(2 );
3368 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
3369 Invalidate();
3370 // Trigger drawing to initialize the mouse rectangle, otherwise the mouse button down
3371 // handler would ignore the mouse event.
3372 Update();
3374 else
3375 ShowFocus( ImplGetFocusRect() );
3377 SetInputContext( InputContext( GetFont() ) );
3378 Button::GetFocus();
3381 void CheckBox::LoseFocus()
3383 if ( ImplGetButtonState() & DrawButtonFlags::Pressed )
3385 ImplGetButtonState() &= ~DrawButtonFlags::Pressed;
3386 Invalidate();
3387 Update();
3390 HideFocus();
3391 Button::LoseFocus();
3393 if (GetText().isEmpty())
3395 // decrease button size again (see GetFocus())
3396 // checkboxes without text will draw focusrect around the check
3397 Point aPos( GetPosPixel() );
3398 Size aSize( GetSizePixel() );
3399 aPos.Move(1,1);
3400 aSize.AdjustHeight( -2 );
3401 aSize.AdjustWidth( -2 );
3402 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
3403 Invalidate();
3407 void CheckBox::StateChanged( StateChangedType nType )
3409 Button::StateChanged( nType );
3411 if ( nType == StateChangedType::State )
3413 if ( IsReallyVisible() && IsUpdateMode() )
3414 Invalidate( maStateRect );
3416 else if ( (nType == StateChangedType::Enable) ||
3417 (nType == StateChangedType::Text) ||
3418 (nType == StateChangedType::Data) ||
3419 (nType == StateChangedType::UpdateMode) )
3421 if ( IsUpdateMode() )
3422 Invalidate();
3424 else if ( nType == StateChangedType::Style )
3426 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
3428 if ( (GetPrevStyle() & CHECKBOX_VIEW_STYLE) !=
3429 (GetStyle() & CHECKBOX_VIEW_STYLE) )
3431 if ( IsUpdateMode() )
3432 Invalidate();
3435 else if ( (nType == StateChangedType::Zoom) ||
3436 (nType == StateChangedType::ControlFont) )
3438 ImplInitSettings( false );
3439 Invalidate();
3441 else if ( nType == StateChangedType::ControlForeground )
3443 ImplInitSettings( false );
3444 Invalidate();
3446 else if ( nType == StateChangedType::ControlBackground )
3448 ImplInitSettings( true );
3449 Invalidate();
3453 void CheckBox::DataChanged( const DataChangedEvent& rDCEvt )
3455 Button::DataChanged( rDCEvt );
3457 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
3458 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
3459 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
3460 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
3462 ImplInitSettings( true );
3463 Invalidate();
3467 bool CheckBox::PreNotify( NotifyEvent& rNEvt )
3469 const MouseEvent* pMouseEvt = nullptr;
3471 if( (rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != nullptr )
3473 if( !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
3475 // trigger redraw if mouse over state has changed
3476 if( IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire) )
3478 if (maMouseRect.IsInside(GetPointerPosPixel()) != maMouseRect.IsInside(GetLastPointerPosPixel()) ||
3479 pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
3481 Invalidate( maStateRect );
3487 return Button::PreNotify(rNEvt);
3490 void CheckBox::Toggle()
3492 ImplCallEventListenersAndHandler( VclEventId::CheckboxToggle, [this] () { maToggleHdl.Call(*this); } );
3495 void CheckBox::SetState( TriState eState )
3497 if ( !mbTriState && (eState == TRISTATE_INDET) )
3498 eState = TRISTATE_FALSE;
3500 if ( meState != eState )
3502 meState = eState;
3503 StateChanged( StateChangedType::State );
3504 Toggle();
3508 bool CheckBox::set_property(const OString &rKey, const OUString &rValue)
3510 if (rKey == "active")
3511 SetState(toBool(rValue) ? TRISTATE_TRUE : TRISTATE_FALSE);
3512 else
3513 return Button::set_property(rKey, rValue);
3514 return true;
3517 void CheckBox::EnableTriState( bool bTriState )
3519 if ( mbTriState != bTriState )
3521 mbTriState = bTriState;
3523 if ( !bTriState && (meState == TRISTATE_INDET) )
3524 SetState( TRISTATE_FALSE );
3528 long CheckBox::ImplGetImageToTextDistance() const
3530 // 4 pixels, but take zoom into account, so the text doesn't "jump" relative to surrounding elements,
3531 // which might have been aligned with the text of the check box
3532 return CalcZoom( 4 );
3535 Size CheckBox::ImplGetCheckImageSize() const
3537 Size aSize;
3538 bool bDefaultSize = true;
3539 if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
3541 ImplControlValue aControlValue;
3542 tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
3543 tools::Rectangle aBoundingRgn, aContentRgn;
3545 // get native size of a check box
3546 if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3547 ControlState::DEFAULT|ControlState::ENABLED,
3548 aControlValue,
3549 aBoundingRgn, aContentRgn ) )
3551 aSize = aContentRgn.GetSize();
3552 bDefaultSize = false;
3555 if( bDefaultSize )
3556 aSize = GetCheckImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
3557 return aSize;
3560 Image CheckBox::GetCheckImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
3562 ImplSVData* pSVData = ImplGetSVData();
3563 const StyleSettings& rStyleSettings = rSettings.GetStyleSettings();
3564 sal_uInt16 nStyle = 0;
3566 if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
3567 nStyle = STYLE_CHECKBOX_MONO;
3569 if ( pSVData->maCtrlData.maCheckImgList.empty() ||
3570 (pSVData->maCtrlData.mnCheckStyle != nStyle) ||
3571 (pSVData->maCtrlData.mnLastCheckFColor != rStyleSettings.GetFaceColor()) ||
3572 (pSVData->maCtrlData.mnLastCheckWColor != rStyleSettings.GetWindowColor()) ||
3573 (pSVData->maCtrlData.mnLastCheckLColor != rStyleSettings.GetLightColor()) )
3575 pSVData->maCtrlData.maCheckImgList.clear();
3577 pSVData->maCtrlData.mnLastCheckFColor = rStyleSettings.GetFaceColor();
3578 pSVData->maCtrlData.mnLastCheckWColor = rStyleSettings.GetWindowColor();
3579 pSVData->maCtrlData.mnLastCheckLColor = rStyleSettings.GetLightColor();
3581 std::vector<OUString> aResources;
3582 if (nStyle)
3584 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO1);
3585 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO2);
3586 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO3);
3587 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO4);
3588 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO5);
3589 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO6);
3590 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO7);
3591 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO8);
3592 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO9);
3594 else
3596 aResources.emplace_back(SV_RESID_BITMAP_CHECK1);
3597 aResources.emplace_back(SV_RESID_BITMAP_CHECK2);
3598 aResources.emplace_back(SV_RESID_BITMAP_CHECK3);
3599 aResources.emplace_back(SV_RESID_BITMAP_CHECK4);
3600 aResources.emplace_back(SV_RESID_BITMAP_CHECK5);
3601 aResources.emplace_back(SV_RESID_BITMAP_CHECK6);
3602 aResources.emplace_back(SV_RESID_BITMAP_CHECK7);
3603 aResources.emplace_back(SV_RESID_BITMAP_CHECK8);
3604 aResources.emplace_back(SV_RESID_BITMAP_CHECK9);
3606 LoadThemedImageList(rStyleSettings, pSVData->maCtrlData.maCheckImgList, aResources);
3607 pSVData->maCtrlData.mnCheckStyle = nStyle;
3610 sal_uInt16 nIndex;
3611 if ( nFlags & DrawButtonFlags::Disabled )
3613 if ( nFlags & DrawButtonFlags::DontKnow )
3614 nIndex = 8;
3615 else if ( nFlags & DrawButtonFlags::Checked )
3616 nIndex = 5;
3617 else
3618 nIndex = 4;
3620 else if ( nFlags & DrawButtonFlags::Pressed )
3622 if ( nFlags & DrawButtonFlags::DontKnow )
3623 nIndex = 7;
3624 else if ( nFlags & DrawButtonFlags::Checked )
3625 nIndex = 3;
3626 else
3627 nIndex = 2;
3629 else
3631 if ( nFlags & DrawButtonFlags::DontKnow )
3632 nIndex = 6;
3633 else if ( nFlags & DrawButtonFlags::Checked )
3634 nIndex = 1;
3635 else
3636 nIndex = 0;
3638 return pSVData->maCtrlData.maCheckImgList[nIndex];
3641 void CheckBox::ImplSetMinimumNWFSize()
3643 Push( PushFlags::MAPMODE );
3644 SetMapMode(MapMode(MapUnit::MapPixel));
3646 ImplControlValue aControlValue;
3647 Size aCurSize( GetSizePixel() );
3648 tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
3649 tools::Rectangle aBoundingRgn, aContentRgn;
3651 // get native size of a radiobutton
3652 if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3653 ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
3654 aBoundingRgn, aContentRgn ) )
3656 Size aSize = aContentRgn.GetSize();
3658 if( aSize.Height() > aCurSize.Height() )
3660 aCurSize.setHeight( aSize.Height() );
3661 SetSizePixel( aCurSize );
3665 Pop();
3668 Size CheckBox::CalcMinimumSize( long nMaxWidth ) const
3670 Size aSize = ImplGetCheckImageSize();
3671 nMaxWidth -= aSize.Width();
3673 OUString aText = GetText();
3674 if (!aText.isEmpty())
3676 // subtract what will be added later
3677 nMaxWidth-=2;
3678 nMaxWidth -= ImplGetImageToTextDistance();
3680 Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
3681 aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
3682 aSize.AdjustWidth(2 ); // for focus rect
3683 aSize.AdjustWidth(ImplGetImageToTextDistance() );
3684 aSize.AdjustWidth(aTextSize.Width() );
3685 if ( aSize.Height() < aTextSize.Height() )
3686 aSize.setHeight( aTextSize.Height() );
3688 else
3690 // is this still correct ? since the checkbox now
3691 // shows a focus rect it should be 2 pixels wider and longer
3692 /* since otherwise the controls in the Writer hang too far up
3693 aSize.Width() += 2;
3694 aSize.Height() += 2;
3698 return CalcWindowSize( aSize );
3701 Size CheckBox::GetOptimalSize() const
3703 int nWidthRequest(get_width_request());
3704 return CalcMinimumSize(nWidthRequest != -1 ? nWidthRequest : 0);
3707 void CheckBox::ShowFocus(const tools::Rectangle& rRect)
3709 if (IsNativeControlSupported(ControlType::Checkbox, ControlPart::Focus))
3711 ImplControlValue aControlValue;
3712 tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
3714 aInRect.SetLeft( rRect.Left() ); // exclude the checkbox itself from the focusrect
3716 DrawNativeControl(ControlType::Checkbox, ControlPart::Focus, aInRect,
3717 ControlState::FOCUSED, aControlValue, OUString());
3719 Button::ShowFocus(rRect);
3722 FactoryFunction CheckBox::GetUITestFactory() const
3724 return CheckBoxUIObject::create;
3727 ImageButton::ImageButton( vcl::Window* pParent, WinBits nStyle ) :
3728 PushButton( pParent, nStyle )
3730 ImplInitStyle();
3733 void ImageButton::ImplInitStyle()
3735 WinBits nStyle = GetStyle();
3737 if ( ! ( nStyle & ( WB_RIGHT | WB_LEFT ) ) )
3738 nStyle |= WB_CENTER;
3740 if ( ! ( nStyle & ( WB_TOP | WB_BOTTOM ) ) )
3741 nStyle |= WB_VCENTER;
3743 SetStyle( nStyle );
3746 ImageRadioButton::ImageRadioButton( vcl::Window* pParent ) :
3747 RadioButton( pParent, 0 )
3751 TriStateBox::TriStateBox( vcl::Window* pParent, WinBits nStyle ) :
3752 CheckBox( pParent, nStyle )
3754 EnableTriState();
3757 DisclosureButton::DisclosureButton( vcl::Window* pParent ) :
3758 CheckBox( pParent, 0 )
3762 void DisclosureButton::ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext)
3764 /* HACK: DisclosureButton is currently assuming, that the disclosure sign
3765 will fit into the rectangle occupied by a normal checkbox on all themes.
3766 If this does not hold true for some theme, ImplGetCheckImageSize
3767 would have to be overridden for DisclosureButton; also GetNativeControlRegion
3768 for ControlType::ListNode would have to be implemented and taken into account
3771 tools::Rectangle aStateRect(GetStateRect());
3773 ImplControlValue aControlValue(GetState() == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
3774 tools::Rectangle aCtrlRegion(aStateRect);
3775 ControlState nState = ControlState::NONE;
3777 if (HasFocus())
3778 nState |= ControlState::FOCUSED;
3779 if (ImplGetButtonState() & DrawButtonFlags::Default)
3780 nState |= ControlState::DEFAULT;
3781 if (Window::IsEnabled())
3782 nState |= ControlState::ENABLED;
3783 if (IsMouseOver() && GetMouseRect().IsInside(GetPointerPosPixel()))
3784 nState |= ControlState::ROLLOVER;
3786 if (rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion,
3787 nState, aControlValue, OUString()))
3788 return;
3790 ImplSVCtrlData& rCtrlData(ImplGetSVData()->maCtrlData);
3791 if (!rCtrlData.mpDisclosurePlus)
3792 rCtrlData.mpDisclosurePlus.reset(new Image(StockImage::Yes, SV_DISCLOSURE_PLUS));
3793 if (!rCtrlData.mpDisclosureMinus)
3794 rCtrlData.mpDisclosureMinus.reset(new Image(StockImage::Yes, SV_DISCLOSURE_MINUS));
3796 Image* pImg
3797 = IsChecked() ? rCtrlData.mpDisclosureMinus.get() : rCtrlData.mpDisclosurePlus.get();
3799 DrawImageFlags nStyle = DrawImageFlags::NONE;
3800 if (!IsEnabled())
3801 nStyle |= DrawImageFlags::Disable;
3803 Size aSize(aStateRect.GetSize());
3804 Size aImgSize(pImg->GetSizePixel());
3805 Point aOff((aSize.Width() - aImgSize.Width()) / 2,
3806 (aSize.Height() - aImgSize.Height()) / 2);
3807 aOff += aStateRect.TopLeft();
3808 rRenderContext.DrawImage(aOff, *pImg, nStyle);
3811 void DisclosureButton::KeyInput( const KeyEvent& rKEvt )
3813 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3815 if( !aKeyCode.GetModifier() &&
3816 ( ( aKeyCode.GetCode() == KEY_ADD ) ||
3817 ( aKeyCode.GetCode() == KEY_SUBTRACT ) )
3820 Check( aKeyCode.GetCode() == KEY_ADD );
3822 else
3823 CheckBox::KeyInput( rKEvt );
3826 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */