1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <tools/poly.hxx>
21 
22 #include <vcl/builder.hxx>
23 #include <vcl/image.hxx>
24 #include <vcl/bitmapex.hxx>
25 #include <vcl/decoview.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/settings.hxx>
29 #include <vcl/toolkit/dialog.hxx>
30 #include <vcl/toolkit/fixed.hxx>
31 #include <vcl/toolkit/button.hxx>
32 #include <vcl/salnativewidgets.hxx>
33 #include <vcl/toolkit/edit.hxx>
34 #include <vcl/layout.hxx>
35 #include <vcl/stdtext.hxx>
36 #include <vcl/uitest/uiobject.hxx>
37 
38 #include <bitmaps.hlst>
39 #include <svdata.hxx>
40 #include <window.h>
41 #include <controldata.hxx>
42 #include <vclstatuslistener.hxx>
43 #include <osl/diagnose.h>
44 
45 #include <comphelper/dispatchcommand.hxx>
46 #include <comphelper/lok.hxx>
47 #include <officecfg/Office/Common.hxx>
48 #include <boost/property_tree/ptree.hpp>
49 #include <tools/json_writer.hxx>
50 
51 
52 using namespace css;
53 
54 constexpr auto PUSHBUTTON_VIEW_STYLE = WB_3DLOOK |
55                                      WB_LEFT | WB_CENTER | WB_RIGHT |
56                                      WB_TOP | WB_VCENTER | WB_BOTTOM |
57                                      WB_WORDBREAK | WB_NOLABEL |
58                                      WB_DEFBUTTON | WB_NOLIGHTBORDER |
59                                      WB_RECTSTYLE | WB_SMALLSTYLE |
60                                      WB_TOGGLE;
61 constexpr auto RADIOBUTTON_VIEW_STYLE = WB_3DLOOK |
62                                      WB_LEFT | WB_CENTER | WB_RIGHT |
63                                      WB_TOP | WB_VCENTER | WB_BOTTOM |
64                                      WB_WORDBREAK | WB_NOLABEL;
65 constexpr auto CHECKBOX_VIEW_STYLE = WB_3DLOOK |
66                                      WB_LEFT | WB_CENTER | WB_RIGHT |
67                                      WB_TOP | WB_VCENTER | WB_BOTTOM |
68                                      WB_WORDBREAK | WB_NOLABEL;
69 
70 #define STYLE_RADIOBUTTON_MONO      (sal_uInt16(0x0001)) // legacy
71 #define STYLE_CHECKBOX_MONO         (sal_uInt16(0x0001)) // legacy
72 
73 class ImplCommonButtonData
74 {
75 public:
76     ImplCommonButtonData();
77 
78     tools::Rectangle       maFocusRect;
79     tools::Long            mnSeparatorX;
80     DrawButtonFlags mnButtonState;
81     bool            mbSmallSymbol;
82     bool            mbGeneratedTooltip;
83 
84     Image           maImage;
85     ImageAlign      meImageAlign;
86     SymbolAlign     meSymbolAlign;
87 
88     Image           maCustomContentImage;
89 
90     /** StatusListener. Updates the button as the slot state changes */
91     rtl::Reference<VclStatusListener<Button>> mpStatusListener;
92 };
93 
ImplCommonButtonData()94 ImplCommonButtonData::ImplCommonButtonData() : maFocusRect(), mnSeparatorX(0), mnButtonState(DrawButtonFlags::NONE),
95 mbSmallSymbol(false), mbGeneratedTooltip(false), maImage(), meImageAlign(ImageAlign::Top), meSymbolAlign(SymbolAlign::LEFT), maCustomContentImage()
96 {
97 }
98 
Button(WindowType nType)99 Button::Button( WindowType nType ) :
100     Control( nType ),
101     mpButtonData( std::make_unique<ImplCommonButtonData>() )
102 {
103 }
104 
~Button()105 Button::~Button()
106 {
107     disposeOnce();
108 }
109 
dispose()110 void Button::dispose()
111 {
112     if (mpButtonData->mpStatusListener.is())
113         mpButtonData->mpStatusListener->dispose();
114     Control::dispose();
115 }
116 
SetCommandHandler(const OUString & aCommand)117 void Button::SetCommandHandler(const OUString& aCommand)
118 {
119     maCommand = aCommand;
120     SetClickHdl( LINK( this, Button, dispatchCommandHandler) );
121 
122     mpButtonData->mpStatusListener = new VclStatusListener<Button>(this, aCommand);
123     mpButtonData->mpStatusListener->startListening();
124 }
125 
Click()126 void Button::Click()
127 {
128     ImplCallEventListenersAndHandler( VclEventId::ButtonClick, [this] () { maClickHdl.Call(this); } );
129 }
130 
SetModeImage(const Image & rImage)131 void Button::SetModeImage( const Image& rImage )
132 {
133     if ( rImage != mpButtonData->maImage )
134     {
135         mpButtonData->maImage = rImage;
136         StateChanged( StateChangedType::Data );
137         queue_resize();
138     }
139 }
140 
GetModeImage() const141 Image const & Button::GetModeImage( ) const
142 {
143     return mpButtonData->maImage;
144 }
145 
HasImage() const146 bool Button::HasImage() const
147 {
148     return !!(mpButtonData->maImage);
149 }
150 
SetImageAlign(ImageAlign eAlign)151 void Button::SetImageAlign( ImageAlign eAlign )
152 {
153     if ( mpButtonData->meImageAlign != eAlign )
154     {
155         mpButtonData->meImageAlign = eAlign;
156         StateChanged( StateChangedType::Data );
157     }
158 }
159 
GetImageAlign() const160 ImageAlign Button::GetImageAlign() const
161 {
162     return mpButtonData->meImageAlign;
163 }
164 
SetCustomButtonImage(const Image & rImage)165 void Button::SetCustomButtonImage(const Image& rImage)
166 {
167     if (rImage != mpButtonData->maCustomContentImage)
168     {
169         mpButtonData->maCustomContentImage = rImage;
170         StateChanged( StateChangedType::Data );
171     }
172 }
173 
GetCustomButtonImage() const174 Image const & Button::GetCustomButtonImage() const
175 {
176     return mpButtonData->maCustomContentImage;
177 }
178 
ImplGetSeparatorX() const179 tools::Long Button::ImplGetSeparatorX() const
180 {
181     return mpButtonData->mnSeparatorX;
182 }
183 
ImplSetSeparatorX(tools::Long nX)184 void Button::ImplSetSeparatorX( tools::Long nX )
185 {
186     mpButtonData->mnSeparatorX = nX;
187 }
188 
ImplGetTextStyle(WinBits nWinStyle,DrawFlags nDrawFlags)189 DrawTextFlags Button::ImplGetTextStyle( WinBits nWinStyle, DrawFlags nDrawFlags )
190 {
191     const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
192     DrawTextFlags nTextStyle = FixedText::ImplGetTextStyle(nWinStyle & ~WB_DEFBUTTON);
193 
194     if (!IsEnabled())
195         nTextStyle |= DrawTextFlags::Disable;
196 
197     if ((nDrawFlags & DrawFlags::Mono) ||
198         (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
199     {
200         nTextStyle |= DrawTextFlags::Mono;
201     }
202 
203     return nTextStyle;
204 }
205 
ImplDrawAlignedImage(OutputDevice * pDev,Point & rPos,Size & rSize,sal_Int32 nImageSep,DrawTextFlags nTextStyle,tools::Rectangle * pSymbolRect,bool bAddImageSep)206 void Button::ImplDrawAlignedImage(OutputDevice* pDev, Point& rPos,
207                                   Size& rSize,
208                                   sal_Int32 nImageSep,
209                                   DrawTextFlags nTextStyle, tools::Rectangle *pSymbolRect,
210                                   bool bAddImageSep)
211 {
212     OUString aText(GetText());
213     bool bDrawImage = HasImage();
214     bool bDrawText  = !aText.isEmpty();
215     bool bHasSymbol = pSymbolRect != nullptr;
216 
217     // No text and no image => nothing to do => return
218     if (!bDrawImage && !bDrawText && !bHasSymbol)
219         return;
220 
221     WinBits nWinStyle = GetStyle();
222     tools::Rectangle aOutRect( rPos, rSize );
223     ImageAlign eImageAlign = mpButtonData->meImageAlign;
224     Size aImageSize = mpButtonData->maImage.GetSizePixel();
225 
226     aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
227     aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
228 
229     // Drawing text or symbol only is simple, use style and output rectangle
230     if (bHasSymbol && !bDrawImage && !bDrawText)
231     {
232         *pSymbolRect = aOutRect;
233         return;
234     }
235     else if (bDrawText && !bDrawImage && !bHasSymbol)
236     {
237         aOutRect = DrawControlText(*pDev, aOutRect, aText, nTextStyle, nullptr, nullptr);
238         tools::Rectangle textRect = GetTextRect(
239             tools::Rectangle(Point(), Size(0x7fffffff, 0x7fffffff)), aText, nTextStyle);
240         // If the button text doesn't fit into it, put it into a tooltip (might happen in sidebar)
241         if (GetQuickHelpText()!= aText && mpButtonData->mbGeneratedTooltip)
242             SetQuickHelpText("");
243         if (GetQuickHelpText().isEmpty() && textRect.getWidth() > rSize.getWidth())
244         {
245             SetQuickHelpText(aText);
246             mpButtonData->mbGeneratedTooltip = true;
247         }
248 
249         ImplSetFocusRect(aOutRect);
250         rSize = aOutRect.GetSize();
251         rPos = aOutRect.TopLeft();
252 
253         return;
254     }
255 
256     // check for HC mode ( image only! )
257     Image* pImage = &(mpButtonData->maImage);
258 
259     Size aTextSize;
260     Size aSymbolSize;
261     Size aDeviceTextSize;
262     Point aImagePos = rPos;
263     Point aTextPos = rPos;
264     tools::Rectangle aUnion(aImagePos, aImageSize);
265     tools::Long nSymbolHeight = 0;
266 
267     if (bDrawText || bHasSymbol)
268     {
269         // Get the size of the text output area ( the symbol will be drawn in
270         // this area as well, so the symbol rectangle will be calculated here, too )
271 
272         tools::Rectangle aRect(Point(), rSize);
273         Size aTSSize;
274 
275         if (bHasSymbol)
276         {
277             tools::Rectangle aSymbol;
278             if (bDrawText)
279             {
280                 nSymbolHeight = pDev->GetTextHeight();
281                 if (mpButtonData->mbSmallSymbol)
282                     nSymbolHeight = nSymbolHeight * 3 / 4;
283 
284                 aSymbol = tools::Rectangle(Point(), Size(nSymbolHeight, nSymbolHeight));
285                 ImplCalcSymbolRect(aSymbol);
286                 aRect.AdjustLeft(3 * nSymbolHeight / 2 );
287                 aTSSize.setWidth( 3 * nSymbolHeight / 2 );
288             }
289             else
290             {
291                 aSymbol = tools::Rectangle(Point(), rSize);
292                 ImplCalcSymbolRect(aSymbol);
293                 aTSSize.setWidth( aSymbol.GetWidth() );
294             }
295             aTSSize.setHeight( aSymbol.GetHeight() );
296             aSymbolSize = aSymbol.GetSize();
297         }
298 
299         if (bDrawText)
300         {
301             if ((eImageAlign == ImageAlign::LeftTop)     ||
302                 (eImageAlign == ImageAlign::Left )        ||
303                 (eImageAlign == ImageAlign::LeftBottom)  ||
304                 (eImageAlign == ImageAlign::RightTop)    ||
305                 (eImageAlign == ImageAlign::Right)        ||
306                 (eImageAlign == ImageAlign::RightBottom))
307             {
308                 aRect.AdjustRight( -sal_Int32(aImageSize.Width() + nImageSep) );
309             }
310             else if ((eImageAlign == ImageAlign::TopLeft)    ||
311                      (eImageAlign == ImageAlign::Top)         ||
312                      (eImageAlign == ImageAlign::TopRight)   ||
313                      (eImageAlign == ImageAlign::BottomLeft) ||
314                      (eImageAlign == ImageAlign::Bottom)      ||
315                      (eImageAlign == ImageAlign::BottomRight))
316             {
317                 aRect.AdjustBottom( -sal_Int32(aImageSize.Height() + nImageSep) );
318             }
319 
320             aRect = GetControlTextRect(*pDev, aRect, aText, nTextStyle, &aDeviceTextSize);
321             aTextSize = aRect.GetSize();
322 
323             aTSSize.AdjustWidth(aTextSize.Width() );
324 
325             if (aTSSize.Height() < aTextSize.Height())
326                 aTSSize.setHeight( aTextSize.Height() );
327 
328             if (bAddImageSep && bDrawImage)
329             {
330                 tools::Long nDiff = (aImageSize.Height() - aTextSize.Height()) / 3;
331                 if (nDiff > 0)
332                     nImageSep += nDiff;
333             }
334         }
335 
336         Size aMax;
337         aMax.setWidth( std::max(aTSSize.Width(), aImageSize.Width()) );
338         aMax.setHeight( std::max(aTSSize.Height(), aImageSize.Height()) );
339 
340         // Now calculate the output area for the image and the text according to the image align flags
341 
342         if ((eImageAlign == ImageAlign::Left) ||
343             (eImageAlign == ImageAlign::Right))
344         {
345             aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
346             aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
347         }
348         else if ((eImageAlign == ImageAlign::LeftBottom) ||
349                  (eImageAlign == ImageAlign::RightBottom))
350         {
351             aImagePos.setY( rPos.Y() + aMax.Height() - aImageSize.Height() );
352             aTextPos.setY( rPos.Y() + aMax.Height() - aTSSize.Height() );
353         }
354         else if ((eImageAlign == ImageAlign::Top) ||
355                  (eImageAlign == ImageAlign::Bottom))
356         {
357             aImagePos.setX( rPos.X() + (aMax.Width() - aImageSize.Width()) / 2 );
358             aTextPos.setX( rPos.X() + (aMax.Width() - aTSSize.Width()) / 2 );
359         }
360         else if ((eImageAlign == ImageAlign::TopRight) ||
361                  (eImageAlign == ImageAlign::BottomRight))
362         {
363             aImagePos.setX( rPos.X() + aMax.Width() - aImageSize.Width() );
364             aTextPos.setX( rPos.X() + aMax.Width() - aTSSize.Width() );
365         }
366 
367         if ((eImageAlign == ImageAlign::LeftTop) ||
368             (eImageAlign == ImageAlign::Left)     ||
369             (eImageAlign == ImageAlign::LeftBottom))
370         {
371             aTextPos.setX( rPos.X() + aImageSize.Width() + nImageSep );
372         }
373         else if ((eImageAlign == ImageAlign::RightTop) ||
374                  (eImageAlign == ImageAlign::Right)     ||
375                  (eImageAlign == ImageAlign::RightBottom))
376         {
377             aImagePos.setX( rPos.X() + aTSSize.Width() + nImageSep );
378         }
379         else if ((eImageAlign == ImageAlign::TopLeft) ||
380                  (eImageAlign == ImageAlign::Top)      ||
381                  (eImageAlign == ImageAlign::TopRight))
382         {
383             aTextPos.setY( rPos.Y() + aImageSize.Height() + nImageSep );
384         }
385         else if ((eImageAlign == ImageAlign::BottomLeft) ||
386                  (eImageAlign == ImageAlign::Bottom)      ||
387                  (eImageAlign == ImageAlign::BottomRight))
388         {
389             aImagePos.setY( rPos.Y() + aTSSize.Height() + nImageSep );
390         }
391         else if (eImageAlign == ImageAlign::Center)
392         {
393             aImagePos.setX( rPos.X() + (aMax.Width()  - aImageSize.Width()) / 2 );
394             aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
395             aTextPos.setX( rPos.X() + (aMax.Width()  - aTSSize.Width()) / 2 );
396             aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
397         }
398         aUnion = tools::Rectangle(aImagePos, aImageSize);
399         aUnion.Union(tools::Rectangle(aTextPos, aTSSize));
400     }
401 
402     // Now place the combination of text and image in the output area of the button
403     // according to the window style (WinBits)
404     tools::Long nXOffset = 0;
405     tools::Long nYOffset = 0;
406 
407     if (nWinStyle & WB_CENTER)
408     {
409         nXOffset = (rSize.Width() - aUnion.GetWidth()) / 2;
410     }
411     else if (nWinStyle & WB_RIGHT)
412     {
413         nXOffset = rSize.Width() - aUnion.GetWidth();
414     }
415 
416     if (nWinStyle & WB_VCENTER)
417     {
418         nYOffset = (rSize.Height() - aUnion.GetHeight()) / 2;
419     }
420     else if (nWinStyle & WB_BOTTOM)
421     {
422         nYOffset = rSize.Height() - aUnion.GetHeight();
423     }
424 
425     // the top left corner should always be visible, so we don't allow negative offsets
426     if (nXOffset < 0) nXOffset = 0;
427     if (nYOffset < 0) nYOffset = 0;
428 
429     aImagePos.AdjustX(nXOffset );
430     aImagePos.AdjustY(nYOffset );
431     aTextPos.AdjustX(nXOffset );
432     aTextPos.AdjustY(nYOffset );
433 
434     // set rPos and rSize to the union
435     rSize = aUnion.GetSize();
436     rPos.AdjustX(nXOffset );
437     rPos.AdjustY(nYOffset );
438 
439     if (bHasSymbol)
440     {
441         if (mpButtonData->meSymbolAlign == SymbolAlign::RIGHT)
442         {
443             Point aRightPos(aTextPos.X() + aTextSize.Width() + aSymbolSize.Width() / 2, aTextPos.Y());
444             *pSymbolRect = tools::Rectangle(aRightPos, aSymbolSize);
445         }
446         else
447         {
448             *pSymbolRect = tools::Rectangle(aTextPos, aSymbolSize);
449             aTextPos.AdjustX(3 * nSymbolHeight / 2 );
450         }
451         if (mpButtonData->mbSmallSymbol)
452         {
453             nYOffset = (aUnion.GetHeight() - aSymbolSize.Height()) / 2;
454             pSymbolRect->setY(aTextPos.Y() + nYOffset);
455         }
456     }
457 
458     DrawImageFlags nStyle = DrawImageFlags::NONE;
459 
460     if (!IsEnabled())
461     {
462         nStyle |= DrawImageFlags::Disable;
463     }
464 
465     if (IsZoom())
466         pDev->DrawImage(aImagePos, aImageSize, *pImage, nStyle);
467     else
468         pDev->DrawImage(aImagePos, *pImage, nStyle);
469 
470     if (bDrawText)
471     {
472         const tools::Rectangle aTOutRect(aTextPos, aTextSize);
473         ImplSetFocusRect(aTOutRect);
474         DrawControlText(*pDev, aTOutRect, aText, nTextStyle, nullptr, nullptr, &aDeviceTextSize);
475     }
476     else
477     {
478         ImplSetFocusRect(tools::Rectangle(aImagePos, aImageSize));
479     }
480 }
481 
ImplSetFocusRect(const tools::Rectangle & rFocusRect)482 void Button::ImplSetFocusRect(const tools::Rectangle &rFocusRect)
483 {
484     tools::Rectangle aFocusRect = rFocusRect;
485     tools::Rectangle aOutputRect(Point(), GetOutputSizePixel());
486 
487     if (!aFocusRect.IsEmpty())
488     {
489         aFocusRect.AdjustLeft( -1 );
490         aFocusRect.AdjustTop( -1 );
491         aFocusRect.AdjustRight( 1 );
492         aFocusRect.AdjustBottom( 1 );
493     }
494 
495     if (aFocusRect.Left()   < aOutputRect.Left())
496         aFocusRect.SetLeft( aOutputRect.Left() );
497     if (aFocusRect.Top()    < aOutputRect.Top())
498         aFocusRect.SetTop( aOutputRect.Top() );
499     if (aFocusRect.Right()  > aOutputRect.Right())
500         aFocusRect.SetRight( aOutputRect.Right() );
501     if (aFocusRect.Bottom() > aOutputRect.Bottom())
502         aFocusRect.SetBottom( aOutputRect.Bottom() );
503 
504     mpButtonData->maFocusRect = aFocusRect;
505 }
506 
ImplGetFocusRect() const507 const tools::Rectangle& Button::ImplGetFocusRect() const
508 {
509     return mpButtonData->maFocusRect;
510 }
511 
GetButtonState()512 DrawButtonFlags& Button::GetButtonState()
513 {
514     return mpButtonData->mnButtonState;
515 }
516 
GetButtonState() const517 DrawButtonFlags Button::GetButtonState() const
518 {
519     return mpButtonData->mnButtonState;
520 }
521 
ImplSetSymbolAlign(SymbolAlign eAlign)522 void Button::ImplSetSymbolAlign( SymbolAlign eAlign )
523 {
524     if ( mpButtonData->meSymbolAlign != eAlign )
525     {
526         mpButtonData->meSymbolAlign = eAlign;
527         StateChanged( StateChangedType::Data );
528     }
529 }
530 
SetSmallSymbol()531 void Button::SetSmallSymbol()
532 {
533     mpButtonData->mbSmallSymbol = true;
534 }
535 
IsSmallSymbol() const536 bool Button::IsSmallSymbol () const
537 {
538     return mpButtonData->mbSmallSymbol;
539 }
540 
set_property(const OString & rKey,const OUString & rValue)541 bool Button::set_property(const OString &rKey, const OUString &rValue)
542 {
543     if (rKey == "image-position")
544     {
545         ImageAlign eAlign = ImageAlign::Left;
546         if (rValue == "left")
547             eAlign = ImageAlign::Left;
548         else if (rValue == "right")
549             eAlign = ImageAlign::Right;
550         else if (rValue == "top")
551             eAlign = ImageAlign::Top;
552         else if (rValue == "bottom")
553             eAlign = ImageAlign::Bottom;
554         SetImageAlign(eAlign);
555     }
556     else if (rKey == "focus-on-click")
557     {
558         WinBits nBits = GetStyle();
559         nBits &= ~WB_NOPOINTERFOCUS;
560         if (!toBool(rValue))
561             nBits |= WB_NOPOINTERFOCUS;
562         SetStyle(nBits);
563     }
564     else
565         return Control::set_property(rKey, rValue);
566     return true;
567 }
568 
statusChanged(const css::frame::FeatureStateEvent & rEvent)569 void Button::statusChanged(const css::frame::FeatureStateEvent& rEvent)
570 {
571     Enable(rEvent.IsEnabled);
572 }
573 
GetUITestFactory() const574 FactoryFunction Button::GetUITestFactory() const
575 {
576     return ButtonUIObject::create;
577 }
578 
DumpAsPropertyTree(tools::JsonWriter & rJsonWriter)579 void Button::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
580 {
581     Control::DumpAsPropertyTree(rJsonWriter);
582     rJsonWriter.put("text", GetText());
583 }
584 
IMPL_STATIC_LINK(Button,dispatchCommandHandler,Button *,pButton,void)585 IMPL_STATIC_LINK( Button, dispatchCommandHandler, Button*, pButton, void )
586 {
587     if (pButton == nullptr)
588         return;
589 
590     comphelper::dispatchCommand(pButton->maCommand, uno::Sequence<beans::PropertyValue>());
591 }
592 
ImplInitPushButtonData()593 void PushButton::ImplInitPushButtonData()
594 {
595     mpWindowImpl->mbPushButton    = true;
596 
597     meSymbol        = SymbolType::DONTKNOW;
598     meState         = TRISTATE_FALSE;
599     mnDDStyle       = PushButtonDropdownStyle::NONE;
600     mbIsActive    = false;
601     mbPressed       = false;
602     mbIsAction      = false;
603 }
604 
605 namespace
606 {
getPreviousSibling(vcl::Window const * pParent)607     vcl::Window* getPreviousSibling(vcl::Window const *pParent)
608     {
609         return pParent ? pParent->GetWindow(GetWindowType::LastChild) : nullptr;
610     }
611 }
612 
ImplInit(vcl::Window * pParent,WinBits nStyle)613 void PushButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
614 {
615     nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
616     Button::ImplInit( pParent, nStyle, nullptr );
617 
618     if ( nStyle & WB_NOLIGHTBORDER )
619         GetButtonState() |= DrawButtonFlags::NoLightBorder;
620 
621     ImplInitSettings( true );
622 }
623 
ImplInitStyle(const vcl::Window * pPrevWindow,WinBits nStyle)624 WinBits PushButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
625 {
626     if ( !(nStyle & WB_NOTABSTOP) )
627         nStyle |= WB_TABSTOP;
628 
629     // if no alignment is given, default to "vertically centered". This is because since
630     // #i26046#, we respect the vertical alignment flags (previously we didn't completely),
631     // but we of course want to look as before when no vertical alignment is specified
632     if ( ( nStyle & ( WB_TOP | WB_VCENTER | WB_BOTTOM ) ) == 0 )
633         nStyle |= WB_VCENTER;
634 
635     if ( !(nStyle & WB_NOGROUP) &&
636          (!pPrevWindow ||
637           ((pPrevWindow->GetType() != WindowType::PUSHBUTTON  ) &&
638            (pPrevWindow->GetType() != WindowType::OKBUTTON    ) &&
639            (pPrevWindow->GetType() != WindowType::CANCELBUTTON) &&
640            (pPrevWindow->GetType() != WindowType::HELPBUTTON  )) ) )
641         nStyle |= WB_GROUP;
642     return nStyle;
643 }
644 
GetCanonicalFont(const StyleSettings & _rStyle) const645 const vcl::Font& PushButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
646 {
647     return _rStyle.GetPushButtonFont();
648 }
649 
GetCanonicalTextColor(const StyleSettings & _rStyle) const650 const Color& PushButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
651 {
652     return _rStyle.GetButtonTextColor();
653 }
654 
ImplInitSettings(bool bBackground)655 void PushButton::ImplInitSettings( bool bBackground )
656 {
657     Button::ImplInitSettings();
658 
659     if ( !bBackground )
660         return;
661 
662     SetBackground();
663     // #i38498#: do not check for GetParent()->IsChildTransparentModeEnabled()
664     // otherwise the formcontrol button will be overdrawn due to ParentClipMode::NoClip
665     // for radio and checkbox this is ok as they should appear transparent in documents
666     if ( IsNativeControlSupported( ControlType::Pushbutton, ControlPart::Entire ) ||
667          (GetStyle() & WB_FLATBUTTON) != 0 )
668     {
669         EnableChildTransparentMode();
670         SetParentClipMode( ParentClipMode::NoClip );
671         SetPaintTransparent( true );
672 
673         if ((GetStyle() & WB_FLATBUTTON) == 0)
674             mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
675         else
676             mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRectsForFlatButtons;
677     }
678     else
679     {
680         EnableChildTransparentMode( false );
681         SetParentClipMode();
682         SetPaintTransparent( false );
683     }
684 }
685 
ImplDrawPushButtonFrame(vcl::RenderContext & rRenderContext,tools::Rectangle & rRect,DrawButtonFlags nStyle)686 void PushButton::ImplDrawPushButtonFrame(vcl::RenderContext& rRenderContext,
687                                          tools::Rectangle& rRect, DrawButtonFlags nStyle)
688 {
689     if (!(GetStyle() & (WB_RECTSTYLE | WB_SMALLSTYLE)))
690     {
691         StyleSettings aStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
692         if (IsControlBackground())
693             aStyleSettings.Set3DColors(GetControlBackground());
694     }
695 
696     DecorationView aDecoView(&rRenderContext);
697     if (IsControlBackground())
698     {
699         AllSettings aSettings = rRenderContext.GetSettings();
700         AllSettings aOldSettings = aSettings;
701         StyleSettings aStyleSettings = aSettings.GetStyleSettings();
702         if (nStyle & DrawButtonFlags::Highlight)
703         {
704             // with the custom background, native highlight do nothing, so code below mimic
705             // native highlight by changing luminance
706             Color controlBackgroundColorHighlighted = GetControlBackground();
707             sal_uInt8 colorLuminance = controlBackgroundColorHighlighted.GetLuminance();
708             if (colorLuminance < 205)
709                 controlBackgroundColorHighlighted.IncreaseLuminance(50);
710             else
711                 controlBackgroundColorHighlighted.DecreaseLuminance(50);
712             aStyleSettings.Set3DColors(controlBackgroundColorHighlighted);
713         }
714         else
715             aStyleSettings.Set3DColors(GetControlBackground());
716         aSettings.SetStyleSettings(aStyleSettings);
717 
718         // Call OutputDevice::SetSettings() explicitly, as rRenderContext may
719         // be a vcl::Window in fact, and vcl::Window::SetSettings() will call
720         // Invalidate(), which is a problem, since we're in Paint().
721         rRenderContext.OutputDevice::SetSettings(aSettings);
722         rRect = aDecoView.DrawButton(rRect, nStyle);
723         rRenderContext.OutputDevice::SetSettings(aOldSettings);
724     }
725     else
726         rRect = aDecoView.DrawButton(rRect, nStyle);
727 }
728 
ImplHitTestPushButton(vcl::Window const * pDev,const Point & rPos)729 bool PushButton::ImplHitTestPushButton( vcl::Window const * pDev,
730                                         const Point& rPos )
731 {
732     tools::Rectangle   aTestRect( Point(), pDev->GetOutputSizePixel() );
733 
734     return aTestRect.IsInside( rPos );
735 }
736 
ImplGetTextStyle(DrawFlags nDrawFlags) const737 DrawTextFlags PushButton::ImplGetTextStyle( DrawFlags nDrawFlags ) const
738 {
739     const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
740 
741     DrawTextFlags nTextStyle = DrawTextFlags::Mnemonic | DrawTextFlags::MultiLine | DrawTextFlags::EndEllipsis;
742 
743     if ( ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono ) ||
744          ( nDrawFlags & DrawFlags::Mono ) )
745         nTextStyle |= DrawTextFlags::Mono;
746 
747     if ( GetStyle() & WB_WORDBREAK )
748         nTextStyle |= DrawTextFlags::WordBreak;
749     if ( GetStyle() & WB_NOLABEL )
750         nTextStyle &= ~DrawTextFlags::Mnemonic;
751 
752     if ( GetStyle() & WB_LEFT )
753         nTextStyle |= DrawTextFlags::Left;
754     else if ( GetStyle() & WB_RIGHT )
755         nTextStyle |= DrawTextFlags::Right;
756     else
757         nTextStyle |= DrawTextFlags::Center;
758 
759     if ( GetStyle() & WB_TOP )
760         nTextStyle |= DrawTextFlags::Top;
761     else if ( GetStyle() & WB_BOTTOM )
762         nTextStyle |= DrawTextFlags::Bottom;
763     else
764         nTextStyle |= DrawTextFlags::VCenter;
765 
766     if ( !IsEnabled() )
767         nTextStyle |= DrawTextFlags::Disable;
768 
769     return nTextStyle;
770 }
771 
ImplDrawPushButtonContent(OutputDevice * pDev,DrawFlags nDrawFlags,const tools::Rectangle & rRect,bool bMenuBtnSep,DrawButtonFlags nButtonFlags)772 void PushButton::ImplDrawPushButtonContent(OutputDevice *pDev, DrawFlags nDrawFlags,
773                                            const tools::Rectangle &rRect, bool bMenuBtnSep,
774                                            DrawButtonFlags nButtonFlags)
775 {
776     const StyleSettings &rStyleSettings = GetSettings().GetStyleSettings();
777     tools::Rectangle aInRect = rRect;
778     Color aColor;
779     DrawTextFlags nTextStyle = ImplGetTextStyle(nDrawFlags);
780     DrawSymbolFlags nStyle;
781 
782     if (aInRect.Right() < aInRect.Left() || aInRect.Bottom() < aInRect.Top())
783         return;
784 
785     pDev->Push(PushFlags::CLIPREGION);
786     pDev->IntersectClipRegion(aInRect);
787 
788     if (nDrawFlags & DrawFlags::Mono)
789         aColor = COL_BLACK;
790 
791     else if (IsControlForeground())
792         aColor = GetControlForeground();
793 
794     // Button types with possibly different text coloring are flat buttons and regular buttons. Regular buttons may be action
795     // buttons and may have an additional default status. Moreover all buttons may have an additional pressed and rollover
796     // (highlight) status. Pressed buttons are always in rollover status.
797 
798     else if (GetStyle() & WB_FLATBUTTON)
799         if (nButtonFlags & DrawButtonFlags::Pressed)
800             aColor = rStyleSettings.GetFlatButtonPressedRolloverTextColor();
801         else if (nButtonFlags & DrawButtonFlags::Highlight)
802             aColor = rStyleSettings.GetFlatButtonRolloverTextColor();
803         else
804             aColor = rStyleSettings.GetFlatButtonTextColor();
805     else
806         if (isAction() && (nButtonFlags & DrawButtonFlags::Default))
807             if (nButtonFlags & DrawButtonFlags::Pressed)
808                 aColor = rStyleSettings.GetDefaultActionButtonPressedRolloverTextColor();
809             else if (nButtonFlags & DrawButtonFlags::Highlight)
810                 aColor = rStyleSettings.GetDefaultActionButtonRolloverTextColor();
811             else
812                 aColor = rStyleSettings.GetDefaultActionButtonTextColor();
813         else if (isAction())
814             if (nButtonFlags & DrawButtonFlags::Pressed)
815                 aColor = rStyleSettings.GetActionButtonPressedRolloverTextColor();
816             else if (nButtonFlags & DrawButtonFlags::Highlight)
817                 aColor = rStyleSettings.GetActionButtonRolloverTextColor();
818             else
819                 aColor = rStyleSettings.GetActionButtonTextColor();
820         else if (nButtonFlags & DrawButtonFlags::Default)
821             if (nButtonFlags & DrawButtonFlags::Pressed)
822                 aColor = rStyleSettings.GetDefaultButtonPressedRolloverTextColor();
823             else if (nButtonFlags & DrawButtonFlags::Highlight)
824                 aColor = rStyleSettings.GetDefaultButtonRolloverTextColor();
825             else
826                 aColor = rStyleSettings.GetDefaultButtonTextColor();
827         else
828             if (nButtonFlags & DrawButtonFlags::Pressed)
829                 aColor = rStyleSettings.GetButtonPressedRolloverTextColor();
830             else if (nButtonFlags & DrawButtonFlags::Highlight)
831                 aColor = rStyleSettings.GetButtonRolloverTextColor();
832             else
833                 aColor = rStyleSettings.GetButtonTextColor();
834 
835     pDev->SetTextColor(aColor);
836 
837     if ( IsEnabled() )
838         nStyle = DrawSymbolFlags::NONE;
839     else
840         nStyle = DrawSymbolFlags::Disable;
841 
842     Size aSize = rRect.GetSize();
843     Point aPos = rRect.TopLeft();
844 
845     sal_Int32 nImageSep = 1 + (pDev->GetTextHeight()-10)/2;
846     if( nImageSep < 1 )
847         nImageSep = 1;
848     if ( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
849          mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
850     {
851         tools::Long nSeparatorX = 0;
852         tools::Rectangle aSymbolRect = aInRect;
853 
854         // calculate symbol size
855         tools::Long nSymbolSize    = pDev->GetTextHeight() / 2 + 1;
856         if (nSymbolSize > aSize.Width() / 2)
857             nSymbolSize = aSize.Width() / 2;
858 
859         nSeparatorX = aInRect.Right() - 2*nSymbolSize;
860 
861         // tdf#141761 Minimum width should be (1) Pixel, see comment
862         // with same task number above for more info
863         const tools::Long nWidthAdjust(2*nSymbolSize);
864         aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
865 
866         // center symbol rectangle in the separated area
867         aSymbolRect.AdjustRight( -(nSymbolSize/2) );
868         aSymbolRect.SetLeft( aSymbolRect.Right() - nSymbolSize );
869 
870         ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
871                               nTextStyle, nullptr, true );
872 
873         tools::Long nDistance = (aSymbolRect.GetHeight() > 10) ? 2 : 1;
874         DecorationView aDecoView( pDev );
875         if( bMenuBtnSep && nSeparatorX > 0 )
876         {
877             Point aStartPt( nSeparatorX, aSymbolRect.Top()+nDistance );
878             Point aEndPt( nSeparatorX, aSymbolRect.Bottom()-nDistance );
879             aDecoView.DrawSeparator( aStartPt, aEndPt );
880         }
881         ImplSetSeparatorX( nSeparatorX );
882 
883         aDecoView.DrawSymbol( aSymbolRect, SymbolType::SPIN_DOWN, aColor, nStyle );
884 
885     }
886     else
887     {
888         tools::Rectangle aSymbolRect;
889         ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
890                               nTextStyle, IsSymbol() ? &aSymbolRect : nullptr, true );
891 
892         if ( IsSymbol() )
893         {
894             DecorationView aDecoView( pDev );
895             aDecoView.DrawSymbol( aSymbolRect, meSymbol, aColor, nStyle );
896         }
897     }
898 
899     pDev->Pop();  // restore clipregion
900 }
901 
ImplDrawPushButton(vcl::RenderContext & rRenderContext)902 void PushButton::ImplDrawPushButton(vcl::RenderContext& rRenderContext)
903 {
904     HideFocus();
905 
906     DrawButtonFlags nButtonStyle = GetButtonState();
907     Size aOutSz(GetOutputSizePixel());
908     tools::Rectangle aRect(Point(), aOutSz);
909     tools::Rectangle aInRect = aRect;
910     bool bNativeOK = false;
911 
912     // adjust style if button should be rendered 'pressed'
913     if (mbPressed || mbIsActive)
914         nButtonStyle |= DrawButtonFlags::Pressed;
915 
916     // TODO: move this to Window class or make it a member !!!
917     ControlType aCtrlType = ControlType::Generic;
918     switch(GetParent()->GetType())
919     {
920         case WindowType::LISTBOX:
921         case WindowType::MULTILISTBOX:
922         case WindowType::TREELISTBOX:
923             aCtrlType = ControlType::Listbox;
924             break;
925 
926         case WindowType::COMBOBOX:
927         case WindowType::PATTERNBOX:
928         case WindowType::NUMERICBOX:
929         case WindowType::METRICBOX:
930         case WindowType::CURRENCYBOX:
931         case WindowType::DATEBOX:
932         case WindowType::TIMEBOX:
933         case WindowType::LONGCURRENCYBOX:
934             aCtrlType = ControlType::Combobox;
935             break;
936         default:
937             break;
938     }
939 
940     bool bDropDown = (IsSymbol() && (GetSymbol() == SymbolType::SPIN_DOWN) && GetText().isEmpty());
941 
942     if( bDropDown && (aCtrlType == ControlType::Combobox || aCtrlType == ControlType::Listbox))
943     {
944         if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::Entire))
945         {
946             // skip painting if the button was already drawn by the theme
947             if (aCtrlType == ControlType::Combobox)
948             {
949                 Edit* pEdit = static_cast<Edit*>(GetParent());
950                 if (pEdit->ImplUseNativeBorder(rRenderContext, pEdit->GetStyle()))
951                     bNativeOK = true;
952             }
953             else if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::HasBackgroundTexture))
954             {
955                 bNativeOK = true;
956             }
957 
958             if (!bNativeOK && GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::ButtonDown))
959             {
960                 // let the theme draw it, note we then need support
961                 // for ControlType::Listbox/ControlPart::ButtonDown and ControlType::Combobox/ControlPart::ButtonDown
962 
963                 ImplControlValue aControlValue;
964                 ControlState nState = ControlState::NONE;
965 
966                 if (mbPressed || mbIsActive)
967                     nState |= ControlState::PRESSED;
968                 if (GetButtonState() & DrawButtonFlags::Pressed)
969                     nState |= ControlState::PRESSED;
970                 if (HasFocus())
971                     nState |= ControlState::FOCUSED;
972                 if (GetButtonState() & DrawButtonFlags::Default)
973                     nState |= ControlState::DEFAULT;
974                 if (Window::IsEnabled())
975                     nState |= ControlState::ENABLED;
976 
977                 if (IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()))
978                     nState |= ControlState::ROLLOVER;
979 
980                 if ( IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()) && mbIsActive)
981                 {
982                     nState |= ControlState::ROLLOVER;
983                     nButtonStyle &= ~DrawButtonFlags::Pressed;
984                 }
985 
986                 bNativeOK = rRenderContext.DrawNativeControl(aCtrlType, ControlPart::ButtonDown, aInRect, nState,
987                                                              aControlValue, OUString());
988             }
989         }
990     }
991 
992     if (bNativeOK)
993         return;
994 
995     bool bRollOver = (IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()));
996     if (bRollOver)
997         nButtonStyle |= DrawButtonFlags::Highlight;
998     bool bDrawMenuSep = mnDDStyle == PushButtonDropdownStyle::SplitMenuButton;
999     if (GetStyle() & WB_FLATBUTTON)
1000     {
1001         if (!bRollOver && !HasFocus())
1002             bDrawMenuSep = false;
1003     }
1004     // tdf#123175 if there is a custom control bg set, draw the button without outsourcing to the NWF
1005     bNativeOK = !IsControlBackground() && rRenderContext.IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire);
1006     if (bNativeOK)
1007     {
1008         PushButtonValue aControlValue;
1009         aControlValue.mbIsAction = isAction();
1010 
1011         tools::Rectangle aCtrlRegion(aInRect);
1012         ControlState nState = ControlState::NONE;
1013 
1014         if (mbPressed || IsChecked() || mbIsActive)
1015         {
1016             nState |= ControlState::PRESSED;
1017             nButtonStyle |= DrawButtonFlags::Pressed;
1018         }
1019         if (GetButtonState() & DrawButtonFlags::Pressed)
1020             nState |= ControlState::PRESSED;
1021         if (HasFocus())
1022             nState |= ControlState::FOCUSED;
1023         if (GetButtonState() & DrawButtonFlags::Default)
1024             nState |= ControlState::DEFAULT;
1025         if (Window::IsEnabled())
1026             nState |= ControlState::ENABLED;
1027 
1028         if (bRollOver || mbIsActive)
1029         {
1030             nButtonStyle |= DrawButtonFlags::Highlight;
1031             nState |= ControlState::ROLLOVER;
1032         }
1033 
1034         if (mbIsActive && bRollOver)
1035         {
1036             nState &= ~ControlState::PRESSED;
1037             nButtonStyle &= ~DrawButtonFlags::Pressed;
1038         }
1039 
1040         if (GetStyle() & WB_FLATBUTTON)
1041             aControlValue.m_bFlatButton = true;
1042         if (GetStyle() & WB_BEVELBUTTON)
1043             aControlValue.mbBevelButton = true;
1044 
1045         // draw frame into invisible window to have aInRect modified correctly
1046         // but do not shift the inner rect for pressed buttons (ie remove DrawButtonFlags::Pressed)
1047         // this assumes the theme has enough visual cues to signalize the button was pressed
1048         //Window aWin( this );
1049         //ImplDrawPushButtonFrame( &aWin, aInRect, nButtonStyle & ~DrawButtonFlags::Pressed );
1050 
1051         // looks better this way as symbols were displaced slightly using the above approach
1052         aInRect.AdjustTop(4 );
1053         aInRect.AdjustBottom( -4 );
1054         aInRect.AdjustLeft(4 );
1055         aInRect.AdjustRight( -4 );
1056 
1057         // prepare single line hint (needed on mac to decide between normal push button and
1058         // rectangular bevel button look)
1059         Size aFontSize(Application::GetSettings().GetStyleSettings().GetPushButtonFont().GetFontSize());
1060         aFontSize = rRenderContext.LogicToPixel(aFontSize, MapMode(MapUnit::MapPoint));
1061         Size aInRectSize(rRenderContext.LogicToPixel(Size(aInRect.GetWidth(), aInRect.GetHeight())));
1062         aControlValue.mbSingleLine = (aInRectSize.Height() < 2 * aFontSize.Height());
1063 
1064         if ((nState & ControlState::ROLLOVER) || !(GetStyle() & WB_FLATBUTTON)
1065             || (HasFocus() && mpWindowImpl->mbUseNativeFocus
1066                 && !IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus)))
1067         {
1068             bNativeOK = rRenderContext.DrawNativeControl(ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion, nState,
1069                                                          aControlValue, OUString() /*PushButton::GetText()*/);
1070         }
1071         else
1072         {
1073             bNativeOK = true;
1074         }
1075 
1076         // draw content using the same aInRect as non-native VCL would do
1077         ImplDrawPushButtonContent(&rRenderContext, DrawFlags::NONE,
1078                                   aInRect, bDrawMenuSep, nButtonStyle);
1079 
1080         if (HasFocus())
1081             ShowFocus(ImplGetFocusRect());
1082     }
1083 
1084     if (bNativeOK)
1085         return;
1086 
1087     // draw PushButtonFrame, aInRect has content size afterwards
1088     if (GetStyle() & WB_FLATBUTTON)
1089     {
1090         tools::Rectangle aTempRect(aInRect);
1091         if (bRollOver)
1092             ImplDrawPushButtonFrame(rRenderContext, aTempRect, nButtonStyle);
1093         aInRect.AdjustLeft(2 );
1094         aInRect.AdjustTop(2 );
1095         aInRect.AdjustRight( -2 );
1096         aInRect.AdjustBottom( -2 );
1097     }
1098     else
1099     {
1100         ImplDrawPushButtonFrame(rRenderContext, aInRect, nButtonStyle);
1101     }
1102 
1103     // draw content
1104     ImplDrawPushButtonContent(&rRenderContext, DrawFlags::NONE, aInRect, bDrawMenuSep, nButtonStyle);
1105 
1106     if (HasFocus())
1107     {
1108         ShowFocus(ImplGetFocusRect());
1109     }
1110 }
1111 
ImplSetDefButton(bool bSet)1112 void PushButton::ImplSetDefButton( bool bSet )
1113 {
1114     Size aSize( GetSizePixel() );
1115     Point aPos( GetPosPixel() );
1116     int dLeft(0), dRight(0), dTop(0), dBottom(0);
1117     bool bSetPos = false;
1118 
1119     if ( IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
1120     {
1121         tools::Rectangle aBound, aCont;
1122         tools::Rectangle aCtrlRegion( 0, 0, 80, 20 ); // use a constant size to avoid accumulating
1123                                              // will not work if the theme has dynamic adornment sizes
1124         ImplControlValue aControlValue;
1125 
1126         // get native size of a 'default' button
1127         // and adjust the VCL button if more space for adornment is required
1128         if( GetNativeControlRegion( ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion,
1129                                     ControlState::DEFAULT|ControlState::ENABLED,
1130                                     aControlValue,
1131                                     aBound, aCont ) )
1132         {
1133             dLeft = aCont.Left() - aBound.Left();
1134             dTop = aCont.Top() - aBound.Top();
1135             dRight = aBound.Right() - aCont.Right();
1136             dBottom = aBound.Bottom() - aCont.Bottom();
1137             bSetPos = dLeft || dTop || dRight || dBottom;
1138         }
1139     }
1140 
1141     if ( bSet )
1142     {
1143         if( !(GetButtonState() & DrawButtonFlags::Default) && bSetPos )
1144         {
1145             // adjust pos/size when toggling from non-default to default
1146             aPos.Move(-dLeft, -dTop);
1147             aSize.AdjustWidth(dLeft + dRight );
1148             aSize.AdjustHeight(dTop + dBottom );
1149         }
1150         GetButtonState() |= DrawButtonFlags::Default;
1151     }
1152     else
1153     {
1154         if( (GetButtonState() & DrawButtonFlags::Default) && bSetPos )
1155         {
1156             // adjust pos/size when toggling from default to non-default
1157             aPos.Move(dLeft, dTop);
1158             aSize.AdjustWidth( -(dLeft + dRight) );
1159             aSize.AdjustHeight( -(dTop + dBottom) );
1160         }
1161         GetButtonState() &= ~DrawButtonFlags::Default;
1162     }
1163     if( bSetPos )
1164         setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
1165 
1166     Invalidate();
1167 }
1168 
ImplIsDefButton() const1169 bool PushButton::ImplIsDefButton() const
1170 {
1171     return bool(GetButtonState() & DrawButtonFlags::Default);
1172 }
1173 
PushButton(WindowType nType)1174 PushButton::PushButton( WindowType nType ) :
1175     Button( nType )
1176 {
1177     ImplInitPushButtonData();
1178 }
1179 
PushButton(vcl::Window * pParent,WinBits nStyle)1180 PushButton::PushButton( vcl::Window* pParent, WinBits nStyle ) :
1181     Button( WindowType::PUSHBUTTON )
1182 {
1183     ImplInitPushButtonData();
1184     ImplInit( pParent, nStyle );
1185 }
1186 
MouseButtonDown(const MouseEvent & rMEvt)1187 void PushButton::MouseButtonDown( const MouseEvent& rMEvt )
1188 {
1189     if ( !(rMEvt.IsLeft() &&
1190          ImplHitTestPushButton( this, rMEvt.GetPosPixel() )) )
1191         return;
1192 
1193     StartTrackingFlags nTrackFlags = StartTrackingFlags::NONE;
1194 
1195     if ( ( GetStyle() & WB_REPEAT ) &&
1196          ! ( GetStyle() & WB_TOGGLE ) )
1197         nTrackFlags |= StartTrackingFlags::ButtonRepeat;
1198 
1199     GetButtonState() |= DrawButtonFlags::Pressed;
1200     Invalidate();
1201     StartTracking( nTrackFlags );
1202 
1203     if ( nTrackFlags & StartTrackingFlags::ButtonRepeat )
1204         Click();
1205 }
1206 
Tracking(const TrackingEvent & rTEvt)1207 void PushButton::Tracking( const TrackingEvent& rTEvt )
1208 {
1209     if ( rTEvt.IsTrackingEnded() )
1210     {
1211         if ( GetButtonState() & DrawButtonFlags::Pressed )
1212         {
1213             if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
1214                 GrabFocus();
1215 
1216             if ( GetStyle() & WB_TOGGLE )
1217             {
1218                 // Don't toggle, when aborted
1219                 if ( !rTEvt.IsTrackingCanceled() )
1220                 {
1221                     if ( IsChecked() )
1222                     {
1223                         Check( false );
1224                         GetButtonState() &= ~DrawButtonFlags::Pressed;
1225                     }
1226                     else
1227                         Check();
1228                 }
1229             }
1230             else
1231                 GetButtonState() &= ~DrawButtonFlags::Pressed;
1232 
1233             Invalidate();
1234 
1235             // do not call Click handler if aborted
1236             if ( !rTEvt.IsTrackingCanceled() )
1237             {
1238                 if ( ! ( GetStyle() & WB_REPEAT ) || ( GetStyle() & WB_TOGGLE ) )
1239                     Click();
1240             }
1241         }
1242     }
1243     else
1244     {
1245         if ( ImplHitTestPushButton( this, rTEvt.GetMouseEvent().GetPosPixel() ) )
1246         {
1247             if ( GetButtonState() & DrawButtonFlags::Pressed )
1248             {
1249                 if ( rTEvt.IsTrackingRepeat() && (GetStyle() & WB_REPEAT) &&
1250                      ! ( GetStyle() & WB_TOGGLE ) )
1251                     Click();
1252             }
1253             else
1254             {
1255                 GetButtonState() |= DrawButtonFlags::Pressed;
1256                 Invalidate();
1257             }
1258         }
1259         else
1260         {
1261             if ( GetButtonState() & DrawButtonFlags::Pressed )
1262             {
1263                 GetButtonState() &= ~DrawButtonFlags::Pressed;
1264                 Invalidate();
1265             }
1266         }
1267     }
1268 }
1269 
KeyInput(const KeyEvent & rKEvt)1270 void PushButton::KeyInput( const KeyEvent& rKEvt )
1271 {
1272     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1273 
1274     if ( !aKeyCode.GetModifier() &&
1275          ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
1276     {
1277         if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
1278         {
1279             GetButtonState() |= DrawButtonFlags::Pressed;
1280             Invalidate();
1281         }
1282 
1283         if ( ( GetStyle() & WB_REPEAT ) &&
1284              ! ( GetStyle() & WB_TOGGLE ) )
1285             Click();
1286     }
1287     else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
1288     {
1289         GetButtonState() &= ~DrawButtonFlags::Pressed;
1290         Invalidate();
1291     }
1292     else
1293         Button::KeyInput( rKEvt );
1294 }
1295 
KeyUp(const KeyEvent & rKEvt)1296 void PushButton::KeyUp( const KeyEvent& rKEvt )
1297 {
1298     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1299 
1300     if ( (GetButtonState() & DrawButtonFlags::Pressed) &&
1301          ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
1302     {
1303         if ( GetStyle() & WB_TOGGLE )
1304         {
1305             if ( IsChecked() )
1306             {
1307                 Check( false );
1308                 GetButtonState() &= ~DrawButtonFlags::Pressed;
1309             }
1310             else
1311                 Check();
1312 
1313             Toggle();
1314         }
1315         else
1316             GetButtonState() &= ~DrawButtonFlags::Pressed;
1317 
1318         Invalidate();
1319 
1320         if ( !( GetStyle() & WB_REPEAT ) || ( GetStyle() & WB_TOGGLE ) )
1321             Click();
1322     }
1323     else
1324         Button::KeyUp( rKEvt );
1325 }
1326 
FillLayoutData() const1327 void PushButton::FillLayoutData() const
1328 {
1329     mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1330     const_cast<PushButton*>(this)->Invalidate();
1331 }
1332 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)1333 void PushButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1334 {
1335     const Image& rCustomButtonImage = GetCustomButtonImage();
1336     if (!!rCustomButtonImage)
1337     {
1338         rRenderContext.DrawImage(Point(0, 0), rCustomButtonImage);
1339         return;
1340     }
1341     ImplDrawPushButton(rRenderContext);
1342 }
1343 
Draw(OutputDevice * pDev,const Point & rPos,DrawFlags nFlags)1344 void PushButton::Draw( OutputDevice* pDev, const Point& rPos,
1345                        DrawFlags nFlags )
1346 {
1347     Point       aPos  = pDev->LogicToPixel( rPos );
1348     Size        aSize = GetSizePixel();
1349     tools::Rectangle   aRect( aPos, aSize );
1350     vcl::Font   aFont = GetDrawPixelFont( pDev );
1351 
1352     pDev->Push();
1353     pDev->SetMapMode();
1354     pDev->SetFont( aFont );
1355 
1356     std::optional<StyleSettings> oOrigDevStyleSettings;
1357 
1358     if ( nFlags & DrawFlags::Mono )
1359     {
1360         pDev->SetTextColor( COL_BLACK );
1361     }
1362     else
1363     {
1364         pDev->SetTextColor( GetTextColor() );
1365         // DecoView uses the FaceColor...
1366         AllSettings aSettings = pDev->GetSettings();
1367         StyleSettings aStyleSettings = aSettings.GetStyleSettings();
1368         oOrigDevStyleSettings = aStyleSettings;
1369         if ( IsControlBackground() )
1370             aStyleSettings.SetFaceColor( GetControlBackground() );
1371         else
1372             aStyleSettings.SetFaceColor( GetSettings().GetStyleSettings().GetFaceColor() );
1373         aSettings.SetStyleSettings( aStyleSettings );
1374         pDev->OutputDevice::SetSettings( aSettings );
1375     }
1376     pDev->SetTextFillColor();
1377 
1378     DecorationView aDecoView( pDev );
1379     DrawButtonFlags nButtonStyle = DrawButtonFlags::NONE;
1380     if ( nFlags & DrawFlags::Mono )
1381         nButtonStyle |= DrawButtonFlags::Mono;
1382     if ( IsChecked() )
1383         nButtonStyle |= DrawButtonFlags::Checked;
1384     aRect = aDecoView.DrawButton( aRect, nButtonStyle );
1385 
1386     ImplDrawPushButtonContent( pDev, nFlags, aRect, true, nButtonStyle );
1387 
1388     // restore original settings (which are not affected by Push/Pop) after
1389     // finished drawing
1390     if (oOrigDevStyleSettings)
1391     {
1392         AllSettings aSettings = pDev->GetSettings();
1393         aSettings.SetStyleSettings(*oOrigDevStyleSettings);
1394         pDev->OutputDevice::SetSettings( aSettings );
1395     }
1396 
1397     pDev->Pop();
1398 }
1399 
Resize()1400 void PushButton::Resize()
1401 {
1402     Control::Resize();
1403     Invalidate();
1404 }
1405 
GetFocus()1406 void PushButton::GetFocus()
1407 {
1408     ShowFocus( ImplGetFocusRect() );
1409     SetInputContext( InputContext( GetFont() ) );
1410     Button::GetFocus();
1411 }
1412 
LoseFocus()1413 void PushButton::LoseFocus()
1414 {
1415     EndSelection();
1416     HideFocus();
1417     Button::LoseFocus();
1418 }
1419 
StateChanged(StateChangedType nType)1420 void PushButton::StateChanged( StateChangedType nType )
1421 {
1422     Button::StateChanged( nType );
1423 
1424     if ( (nType == StateChangedType::Enable) ||
1425          (nType == StateChangedType::Text) ||
1426          (nType == StateChangedType::Data) ||
1427          (nType == StateChangedType::State) ||
1428          (nType == StateChangedType::UpdateMode) )
1429     {
1430         if ( IsReallyVisible() && IsUpdateMode() )
1431             Invalidate();
1432     }
1433     else if ( nType == StateChangedType::Style )
1434     {
1435         SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
1436 
1437         bool bIsDefButton = ( GetStyle() & WB_DEFBUTTON ) != 0;
1438         bool bWasDefButton = ( GetPrevStyle() & WB_DEFBUTTON ) != 0;
1439         if ( bIsDefButton != bWasDefButton )
1440             ImplSetDefButton( bIsDefButton );
1441 
1442         if ( IsReallyVisible() && IsUpdateMode() )
1443         {
1444             if ( (GetPrevStyle() & PUSHBUTTON_VIEW_STYLE) !=
1445                  (GetStyle() & PUSHBUTTON_VIEW_STYLE) )
1446                 Invalidate();
1447         }
1448     }
1449     else if ( (nType == StateChangedType::Zoom) ||
1450               (nType == StateChangedType::ControlFont) )
1451     {
1452         ImplInitSettings( false );
1453         Invalidate();
1454     }
1455     else if ( nType == StateChangedType::ControlForeground )
1456     {
1457         ImplInitSettings( false );
1458         Invalidate();
1459     }
1460     else if ( nType == StateChangedType::ControlBackground )
1461     {
1462         ImplInitSettings( true );
1463         Invalidate();
1464     }
1465 }
1466 
DataChanged(const DataChangedEvent & rDCEvt)1467 void PushButton::DataChanged( const DataChangedEvent& rDCEvt )
1468 {
1469     Button::DataChanged( rDCEvt );
1470 
1471     if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1472          (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1473          ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1474           (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1475     {
1476         ImplInitSettings( true );
1477         Invalidate();
1478     }
1479 }
1480 
PreNotify(NotifyEvent & rNEvt)1481 bool PushButton::PreNotify( NotifyEvent& rNEvt )
1482 {
1483     if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
1484     {
1485         const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
1486         if( pMouseEvt && (pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow()) )
1487         {
1488             // trigger redraw as mouse over state has changed
1489 
1490             // TODO: move this to Window class or make it a member !!!
1491             ControlType aCtrlType = ControlType::Generic;
1492             switch( GetParent()->GetType() )
1493             {
1494                 case WindowType::LISTBOX:
1495                 case WindowType::MULTILISTBOX:
1496                 case WindowType::TREELISTBOX:
1497                     aCtrlType = ControlType::Listbox;
1498                     break;
1499 
1500                 case WindowType::COMBOBOX:
1501                 case WindowType::PATTERNBOX:
1502                 case WindowType::NUMERICBOX:
1503                 case WindowType::METRICBOX:
1504                 case WindowType::CURRENCYBOX:
1505                 case WindowType::DATEBOX:
1506                 case WindowType::TIMEBOX:
1507                 case WindowType::LONGCURRENCYBOX:
1508                     aCtrlType = ControlType::Combobox;
1509                     break;
1510                 default:
1511                     break;
1512             }
1513 
1514             bool bDropDown = ( IsSymbol() && (GetSymbol()==SymbolType::SPIN_DOWN) && GetText().isEmpty() );
1515 
1516             if( bDropDown && GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::Entire) &&
1517                    !GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::ButtonDown) )
1518             {
1519                 vcl::Window *pBorder = GetParent()->GetWindow( GetWindowType::Border );
1520                 if(aCtrlType == ControlType::Combobox)
1521                 {
1522                     // only paint the button part to avoid flickering of the combobox text
1523                     tools::Rectangle aClipRect( Point(), GetOutputSizePixel() );
1524                     aClipRect.SetPos(pBorder->ScreenToOutputPixel(OutputToScreenPixel(aClipRect.TopLeft())));
1525                     pBorder->Invalidate( aClipRect );
1526                 }
1527                 else
1528                 {
1529                     pBorder->Invalidate( InvalidateFlags::NoErase );
1530                 }
1531             }
1532             else if( (GetStyle() & WB_FLATBUTTON) ||
1533                      IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
1534             {
1535                 Invalidate();
1536             }
1537         }
1538     }
1539 
1540     return Button::PreNotify(rNEvt);
1541 }
1542 
Toggle()1543 void PushButton::Toggle()
1544 {
1545     ImplCallEventListenersAndHandler( VclEventId::PushbuttonToggle, nullptr );
1546 }
1547 
SetSymbol(SymbolType eSymbol)1548 void PushButton::SetSymbol( SymbolType eSymbol )
1549 {
1550     if ( meSymbol != eSymbol )
1551     {
1552         meSymbol = eSymbol;
1553         CompatStateChanged( StateChangedType::Data );
1554     }
1555 }
1556 
SetSymbolAlign(SymbolAlign eAlign)1557 void PushButton::SetSymbolAlign( SymbolAlign eAlign )
1558 {
1559     ImplSetSymbolAlign( eAlign );
1560 }
1561 
SetDropDown(PushButtonDropdownStyle nStyle)1562 void PushButton::SetDropDown( PushButtonDropdownStyle nStyle )
1563 {
1564     if ( mnDDStyle != nStyle )
1565     {
1566         mnDDStyle = nStyle;
1567         CompatStateChanged( StateChangedType::Data );
1568     }
1569 }
1570 
SetState(TriState eState)1571 void PushButton::SetState( TriState eState )
1572 {
1573     if ( meState == eState )
1574         return;
1575 
1576     meState = eState;
1577     if ( meState == TRISTATE_FALSE )
1578         GetButtonState() &= ~DrawButtonFlags(DrawButtonFlags::Checked | DrawButtonFlags::DontKnow);
1579     else if ( meState == TRISTATE_TRUE )
1580     {
1581         GetButtonState() &= ~DrawButtonFlags::DontKnow;
1582         GetButtonState() |= DrawButtonFlags::Checked;
1583     }
1584     else // TRISTATE_INDET
1585     {
1586         GetButtonState() &= ~DrawButtonFlags::Checked;
1587         GetButtonState() |= DrawButtonFlags::DontKnow;
1588     }
1589 
1590     CompatStateChanged( StateChangedType::State );
1591     Toggle();
1592 }
1593 
statusChanged(const css::frame::FeatureStateEvent & rEvent)1594 void PushButton::statusChanged(const css::frame::FeatureStateEvent& rEvent)
1595 {
1596     Button::statusChanged(rEvent);
1597     if (rEvent.State.has<bool>())
1598         SetPressed(rEvent.State.get<bool>());
1599 }
1600 
SetPressed(bool bPressed)1601 void PushButton::SetPressed( bool bPressed )
1602 {
1603     if ( mbPressed != bPressed )
1604     {
1605         mbPressed = bPressed;
1606         CompatStateChanged( StateChangedType::Data );
1607     }
1608 }
1609 
EndSelection()1610 void PushButton::EndSelection()
1611 {
1612     EndTracking( TrackingEventFlags::Cancel );
1613     if ( !isDisposed() &&
1614          GetButtonState() & DrawButtonFlags::Pressed )
1615     {
1616         GetButtonState() &= ~DrawButtonFlags::Pressed;
1617         if ( !mbPressed )
1618             Invalidate();
1619     }
1620 }
1621 
CalcMinimumSize() const1622 Size PushButton::CalcMinimumSize() const
1623 {
1624     Size aSize;
1625 
1626     if ( IsSymbol() )
1627     {
1628         if ( IsSmallSymbol ())
1629             aSize = Size( 16, 12 );
1630         else
1631             aSize = Size( 26, 24 );
1632     }
1633     else if ( Button::HasImage() )
1634         aSize = GetModeImage().GetSizePixel();
1635     if( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
1636         mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
1637     {
1638         tools::Long nSymbolSize = GetTextHeight() / 2 + 1;
1639         aSize.AdjustWidth(2*nSymbolSize );
1640     }
1641     if (!PushButton::GetText().isEmpty())
1642     {
1643         Size textSize = GetTextRect( tools::Rectangle( Point(), Size( 0x7fffffff, 0x7fffffff ) ),
1644                                      PushButton::GetText(), ImplGetTextStyle( DrawFlags::NONE ) ).GetSize();
1645 
1646         tools::Long nTextHeight = textSize.Height() * 1.15;
1647 
1648         ImageAlign eImageAlign = GetImageAlign();
1649         // tdf#142337 only considering the simple top/bottom/left/right possibilities
1650         if (eImageAlign == ImageAlign::Top || eImageAlign == ImageAlign::Bottom)
1651         {
1652             aSize.AdjustHeight(nTextHeight);
1653             aSize.setWidth(std::max(aSize.Width(), textSize.Width()));
1654         }
1655         else
1656         {
1657             aSize.AdjustWidth(textSize.Width());
1658             aSize.setHeight(std::max(aSize.Height(), nTextHeight));
1659         }
1660     }
1661 
1662     // cf. ImplDrawPushButton ...
1663     if( (GetStyle() & WB_SMALLSTYLE) == 0 )
1664     {
1665         aSize.AdjustWidth(24 );
1666         aSize.AdjustHeight(12 );
1667     }
1668 
1669     return CalcWindowSize( aSize );
1670 }
1671 
GetOptimalSize() const1672 Size PushButton::GetOptimalSize() const
1673 {
1674     return CalcMinimumSize();
1675 }
1676 
set_property(const OString & rKey,const OUString & rValue)1677 bool PushButton::set_property(const OString &rKey, const OUString &rValue)
1678 {
1679     if (rKey == "has-default")
1680     {
1681         WinBits nBits = GetStyle();
1682         nBits &= ~WB_DEFBUTTON;
1683         if (toBool(rValue))
1684             nBits |= WB_DEFBUTTON;
1685         SetStyle(nBits);
1686     }
1687     else
1688         return Button::set_property(rKey, rValue);
1689     return true;
1690 }
1691 
ShowFocus(const tools::Rectangle & rRect)1692 void PushButton::ShowFocus(const tools::Rectangle& rRect)
1693 {
1694     if (IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
1695     {
1696         PushButtonValue aControlValue;
1697         aControlValue.mbIsAction = isAction();
1698         tools::Rectangle aInRect(Point(), GetOutputSizePixel());
1699         GetOutDev()->DrawNativeControl(ControlType::Pushbutton, ControlPart::Focus, aInRect,
1700                                        ControlState::FOCUSED, aControlValue, OUString());
1701     }
1702     Button::ShowFocus(rRect);
1703 }
1704 
ImplInit(vcl::Window * pParent,WinBits nStyle)1705 void OKButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1706 {
1707     set_id("ok");
1708     PushButton::ImplInit( pParent, nStyle );
1709 
1710     SetText( GetStandardText( StandardButtonType::OK ) );
1711 }
1712 
OKButton(vcl::Window * pParent,WinBits nStyle)1713 OKButton::OKButton( vcl::Window* pParent, WinBits nStyle ) :
1714     PushButton( WindowType::OKBUTTON )
1715 {
1716     ImplInit( pParent, nStyle );
1717 }
1718 
Click()1719 void OKButton::Click()
1720 {
1721     // close parent if no link set
1722     if ( !GetClickHdl() )
1723     {
1724         vcl::Window* pParent = getNonLayoutParent(this);
1725         if ( pParent->IsSystemWindow() )
1726         {
1727             if ( pParent->IsDialog() )
1728             {
1729                 VclPtr<Dialog> xParent( static_cast<Dialog*>(pParent) );
1730                 if ( xParent->IsInExecute() )
1731                     xParent->EndDialog( RET_OK );
1732                 // prevent recursive calls
1733                 else if ( !xParent->IsInClose() )
1734                 {
1735                     if ( pParent->GetStyle() & WB_CLOSEABLE )
1736                         xParent->Close();
1737                 }
1738             }
1739             else
1740             {
1741                 if ( pParent->GetStyle() & WB_CLOSEABLE )
1742                     static_cast<SystemWindow*>(pParent)->Close();
1743             }
1744         }
1745     }
1746     else
1747     {
1748         PushButton::Click();
1749     }
1750 }
1751 
ImplInit(vcl::Window * pParent,WinBits nStyle)1752 void CancelButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1753 {
1754     set_id("cancel");
1755     PushButton::ImplInit( pParent, nStyle );
1756 
1757     SetText( GetStandardText( StandardButtonType::Cancel ) );
1758 }
1759 
CancelButton(vcl::Window * pParent,WinBits nStyle)1760 CancelButton::CancelButton( vcl::Window* pParent, WinBits nStyle ) :
1761     PushButton( WindowType::CANCELBUTTON )
1762 {
1763     ImplInit( pParent, nStyle );
1764 }
1765 
Click()1766 void CancelButton::Click()
1767 {
1768     // close parent if link not set
1769     if ( !GetClickHdl() )
1770     {
1771         vcl::Window* pParent = getNonLayoutParent(this);
1772         if ( pParent->IsSystemWindow() )
1773         {
1774             if ( pParent->IsDialog() )
1775             {
1776                 if ( static_cast<Dialog*>(pParent)->IsInExecute() )
1777                     static_cast<Dialog*>(pParent)->EndDialog();
1778                 // prevent recursive calls
1779                 else if ( !static_cast<Dialog*>(pParent)->IsInClose() )
1780                 {
1781                     if ( pParent->GetStyle() & WB_CLOSEABLE )
1782                         static_cast<Dialog*>(pParent)->Close();
1783                 }
1784             }
1785             else
1786             {
1787                 if ( pParent->GetStyle() & WB_CLOSEABLE )
1788                     static_cast<SystemWindow*>(pParent)->Close();
1789             }
1790         }
1791     }
1792     else
1793     {
1794         PushButton::Click();
1795     }
1796 }
1797 
CloseButton(vcl::Window * pParent)1798 CloseButton::CloseButton( vcl::Window* pParent )
1799     : CancelButton(pParent, 0)
1800 {
1801     SetText( GetStandardText( StandardButtonType::Close ) );
1802 }
1803 
ImplInit(vcl::Window * pParent,WinBits nStyle)1804 void HelpButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1805 {
1806     set_id("help");
1807     PushButton::ImplInit( pParent, nStyle | WB_NOPOINTERFOCUS );
1808 
1809     SetText( GetStandardText( StandardButtonType::Help ) );
1810 }
1811 
HelpButton(vcl::Window * pParent,WinBits nStyle)1812 HelpButton::HelpButton( vcl::Window* pParent, WinBits nStyle ) :
1813     PushButton( WindowType::HELPBUTTON )
1814 {
1815     ImplInit( pParent, nStyle );
1816 }
1817 
Click()1818 void HelpButton::Click()
1819 {
1820     // trigger help if no link set
1821     if ( !GetClickHdl() )
1822     {
1823         vcl::Window* pFocusWin = Application::GetFocusWindow();
1824         if ( !pFocusWin || comphelper::LibreOfficeKit::isActive() )
1825             pFocusWin = this;
1826 
1827         HelpEvent aEvt( pFocusWin->GetPointerPosPixel(), HelpEventMode::CONTEXT );
1828         pFocusWin->RequestHelp( aEvt );
1829     }
1830     PushButton::Click();
1831 }
1832 
StateChanged(StateChangedType nStateChange)1833 void HelpButton::StateChanged( StateChangedType nStateChange )
1834 {
1835     // Hide when we have no help URL.
1836     if (comphelper::LibreOfficeKit::isActive() &&
1837         officecfg::Office::Common::Help::HelpRootURL::get().isEmpty())
1838         Hide();
1839     else
1840         PushButton::StateChanged(nStateChange);
1841 }
1842 
ImplInitRadioButtonData()1843 void RadioButton::ImplInitRadioButtonData()
1844 {
1845     mbChecked       = false;
1846     mbRadioCheck    = true;
1847     mbStateChanged  = false;
1848 }
1849 
ImplInit(vcl::Window * pParent,WinBits nStyle)1850 void RadioButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1851 {
1852     nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
1853     Button::ImplInit( pParent, nStyle, nullptr );
1854 
1855     ImplInitSettings( true );
1856 }
1857 
ImplInitStyle(const vcl::Window * pPrevWindow,WinBits nStyle)1858 WinBits RadioButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
1859 {
1860     if ( !(nStyle & WB_NOGROUP) &&
1861          (!pPrevWindow || (pPrevWindow->GetType() != WindowType::RADIOBUTTON)) )
1862         nStyle |= WB_GROUP;
1863     if ( !(nStyle & WB_NOTABSTOP) )
1864     {
1865         if ( IsChecked() )
1866             nStyle |= WB_TABSTOP;
1867         else
1868             nStyle &= ~WB_TABSTOP;
1869     }
1870 
1871     return nStyle;
1872 }
1873 
GetCanonicalFont(const StyleSettings & _rStyle) const1874 const vcl::Font& RadioButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
1875 {
1876     return _rStyle.GetRadioCheckFont();
1877 }
1878 
GetCanonicalTextColor(const StyleSettings & _rStyle) const1879 const Color& RadioButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
1880 {
1881     return _rStyle.GetRadioCheckTextColor();
1882 }
1883 
ImplInitSettings(bool bBackground)1884 void RadioButton::ImplInitSettings( bool bBackground )
1885 {
1886     Button::ImplInitSettings();
1887 
1888     if ( !bBackground )
1889         return;
1890 
1891     vcl::Window* pParent = GetParent();
1892     if ( !IsControlBackground() &&
1893         (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) ) )
1894     {
1895         EnableChildTransparentMode();
1896         SetParentClipMode( ParentClipMode::NoClip );
1897         SetPaintTransparent( true );
1898         SetBackground();
1899         if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
1900             mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
1901     }
1902     else
1903     {
1904         EnableChildTransparentMode( false );
1905         SetParentClipMode();
1906         SetPaintTransparent( false );
1907 
1908         if ( IsControlBackground() )
1909             SetBackground( GetControlBackground() );
1910         else
1911             SetBackground( pParent->GetBackground() );
1912     }
1913 }
1914 
ImplDrawRadioButtonState(vcl::RenderContext & rRenderContext)1915 void RadioButton::ImplDrawRadioButtonState(vcl::RenderContext& rRenderContext)
1916 {
1917     bool bNativeOK = false;
1918 
1919     // no native drawing for image radio buttons
1920     if (!maImage && rRenderContext.IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire))
1921     {
1922         ImplControlValue aControlValue( mbChecked ? ButtonValue::On : ButtonValue::Off );
1923         tools::Rectangle aCtrlRect(maStateRect.TopLeft(), maStateRect.GetSize());
1924         ControlState nState = ControlState::NONE;
1925 
1926         if (GetButtonState() & DrawButtonFlags::Pressed)
1927             nState |= ControlState::PRESSED;
1928         if (HasFocus())
1929             nState |= ControlState::FOCUSED;
1930         if (GetButtonState() & DrawButtonFlags::Default)
1931             nState |= ControlState::DEFAULT;
1932         if (IsEnabled())
1933             nState |= ControlState::ENABLED;
1934 
1935         if (IsMouseOver() && maMouseRect.IsInside(GetPointerPosPixel()))
1936             nState |= ControlState::ROLLOVER;
1937 
1938         bNativeOK = rRenderContext.DrawNativeControl(ControlType::Radiobutton, ControlPart::Entire, aCtrlRect,
1939                                                      nState, aControlValue, OUString());
1940     }
1941 
1942     if (bNativeOK)
1943         return;
1944 
1945     if (!maImage)
1946     {
1947         DrawButtonFlags nStyle = GetButtonState();
1948         if (!IsEnabled())
1949             nStyle |= DrawButtonFlags::Disabled;
1950         if (mbChecked)
1951             nStyle |= DrawButtonFlags::Checked;
1952         Image aImage = GetRadioImage(rRenderContext.GetSettings(), nStyle);
1953         if (IsZoom())
1954             rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
1955         else
1956             rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
1957     }
1958     else
1959     {
1960         HideFocus();
1961 
1962         DecorationView aDecoView(&rRenderContext);
1963         const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1964         tools::Rectangle aImageRect  = maStateRect;
1965         Size aImageSize = maImage.GetSizePixel();
1966         bool bEnabled = IsEnabled();
1967 
1968         aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
1969         aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
1970 
1971         aImageRect.AdjustLeft( 1 );
1972         aImageRect.AdjustTop( 1 );
1973         aImageRect.AdjustRight( -1 );
1974         aImageRect.AdjustBottom( -1 );
1975 
1976         // display border and selection status
1977         aImageRect = aDecoView.DrawFrame(aImageRect, DrawFrameStyle::DoubleIn);
1978         if ((GetButtonState() & DrawButtonFlags::Pressed) || !bEnabled)
1979             rRenderContext.SetFillColor( rStyleSettings.GetFaceColor());
1980         else
1981             rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
1982         rRenderContext.SetLineColor();
1983         rRenderContext.DrawRect(aImageRect);
1984 
1985         // display image
1986         DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1987         if (!bEnabled)
1988             nImageStyle |= DrawImageFlags::Disable;
1989 
1990         Image* pImage = &maImage;
1991 
1992         Point aImagePos(aImageRect.TopLeft());
1993         aImagePos.AdjustX((aImageRect.GetWidth() - aImageSize.Width()) / 2 );
1994         aImagePos.AdjustY((aImageRect.GetHeight() - aImageSize.Height()) / 2 );
1995         if (IsZoom())
1996             rRenderContext.DrawImage(aImagePos, aImageSize, *pImage, nImageStyle);
1997         else
1998             rRenderContext.DrawImage(aImagePos, *pImage, nImageStyle);
1999 
2000         aImageRect.AdjustLeft( 1 );
2001         aImageRect.AdjustTop( 1 );
2002         aImageRect.AdjustRight( -1 );
2003         aImageRect.AdjustBottom( -1 );
2004 
2005         ImplSetFocusRect(aImageRect);
2006 
2007         if (mbChecked)
2008         {
2009             rRenderContext.SetLineColor(rStyleSettings.GetHighlightColor());
2010             rRenderContext.SetFillColor();
2011             if ((aImageSize.Width() >= 20) || (aImageSize.Height() >= 20))
2012             {
2013                 aImageRect.AdjustLeft( 1 );
2014                 aImageRect.AdjustTop( 1 );
2015                 aImageRect.AdjustRight( -1 );
2016                 aImageRect.AdjustBottom( -1 );
2017             }
2018             rRenderContext.DrawRect(aImageRect);
2019             aImageRect.AdjustLeft( 1 );
2020             aImageRect.AdjustTop( 1 );
2021             aImageRect.AdjustRight( -1 );
2022             aImageRect.AdjustBottom( -1 );
2023             rRenderContext.DrawRect(aImageRect);
2024         }
2025 
2026         if (HasFocus())
2027             ShowFocus(ImplGetFocusRect());
2028     }
2029 }
2030 
2031 // for drawing RadioButton or CheckButton that has Text and/or Image
ImplDrawRadioCheck(OutputDevice * pDev,WinBits nWinStyle,DrawFlags nDrawFlags,const Point & rPos,const Size & rSize,const Size & rImageSize,tools::Rectangle & rStateRect,tools::Rectangle & rMouseRect)2032 void Button::ImplDrawRadioCheck(OutputDevice* pDev, WinBits nWinStyle, DrawFlags nDrawFlags,
2033                                 const Point& rPos, const Size& rSize,
2034                                 const Size& rImageSize, tools::Rectangle& rStateRect,
2035                                 tools::Rectangle& rMouseRect)
2036 {
2037     DrawTextFlags nTextStyle = Button::ImplGetTextStyle( nWinStyle, nDrawFlags );
2038 
2039     const tools::Long nImageSep = GetDrawPixel( pDev, ImplGetImageToTextDistance() );
2040     Size aSize( rSize );
2041     Point aPos( rPos );
2042     aPos.AdjustX(rImageSize.Width() + nImageSep );
2043 
2044     // tdf#141761 Old (convenience?) adjustment of width may lead to empty
2045     // or negative(!) Size, that needs to be avoided. The coordinate context
2046     // is pixel-oriented (all Paints of Controls are, historically), so
2047     // the minimum width should be '1' Pixel.
2048     // Hint: nImageSep is based on Zoom (using Window::CalcZoom) and
2049     // MapModes (using Window::GetDrawPixel) - so potentially a wide range
2050     // of unpredictable values is possible
2051     const tools::Long nWidthAdjust(rImageSize.Width() + nImageSep);
2052     aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
2053 
2054     // if the text rect height is smaller than the height of the image
2055     // then for single lines the default should be centered text
2056     if( (nWinStyle & (WB_TOP|WB_VCENTER|WB_BOTTOM)) == 0 &&
2057         (rImageSize.Height() > rSize.Height() || ! (nWinStyle & WB_WORDBREAK) ) )
2058     {
2059         nTextStyle &= ~DrawTextFlags(DrawTextFlags::Top|DrawTextFlags::Bottom);
2060         nTextStyle |= DrawTextFlags::VCenter;
2061         aSize.setHeight( rImageSize.Height() );
2062     }
2063 
2064     ImplDrawAlignedImage( pDev, aPos, aSize, 1, nTextStyle );
2065 
2066     rMouseRect = tools::Rectangle( aPos, aSize );
2067     rMouseRect.SetLeft( rPos.X() );
2068 
2069     rStateRect.SetLeft( rPos.X() );
2070     rStateRect.SetTop( rMouseRect.Top() );
2071 
2072     if ( aSize.Height() > rImageSize.Height() )
2073         rStateRect.AdjustTop(( aSize.Height() - rImageSize.Height() ) / 2 );
2074     else
2075     {
2076         rStateRect.AdjustTop( -(( rImageSize.Height() - aSize.Height() ) / 2) );
2077         if( rStateRect.Top() < 0 )
2078             rStateRect.SetTop( 0 );
2079     }
2080 
2081     rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
2082     rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
2083 
2084     if ( rStateRect.Bottom() > rMouseRect.Bottom() )
2085         rMouseRect.SetBottom( rStateRect.Bottom() );
2086 }
2087 
ImplDraw(OutputDevice * pDev,DrawFlags nDrawFlags,const Point & rPos,const Size & rSize,const Size & rImageSize,tools::Rectangle & rStateRect,tools::Rectangle & rMouseRect)2088 void RadioButton::ImplDraw( OutputDevice* pDev, DrawFlags nDrawFlags,
2089                             const Point& rPos, const Size& rSize,
2090                             const Size& rImageSize, tools::Rectangle& rStateRect,
2091                             tools::Rectangle& rMouseRect )
2092 {
2093     WinBits                 nWinStyle = GetStyle();
2094     OUString                aText( GetText() );
2095 
2096     pDev->Push( PushFlags::CLIPREGION );
2097     pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
2098 
2099     // no image radio button
2100     if ( !maImage )
2101     {
2102         if (!aText.isEmpty() || HasImage())
2103         {
2104             Button::ImplDrawRadioCheck(pDev, nWinStyle, nDrawFlags,
2105                                        rPos, rSize, rImageSize,
2106                                        rStateRect, rMouseRect);
2107         }
2108         else
2109         {
2110             rStateRect.SetLeft( rPos.X() );
2111             if ( nWinStyle & WB_VCENTER )
2112                 rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
2113             else if ( nWinStyle & WB_BOTTOM )
2114                 rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() ); //-1;
2115             else
2116                 rStateRect.SetTop( rPos.Y() );
2117             rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
2118             rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
2119             rMouseRect          = rStateRect;
2120 
2121             ImplSetFocusRect( rStateRect );
2122         }
2123     }
2124     else
2125     {
2126         bool        bTopImage   = (nWinStyle & WB_TOP) != 0;
2127         Size        aImageSize  = maImage.GetSizePixel();
2128         tools::Rectangle   aImageRect( rPos, rSize );
2129         tools::Long        nTextHeight = pDev->GetTextHeight();
2130         tools::Long        nTextWidth  = pDev->GetCtrlTextWidth( aText );
2131 
2132         // calculate position and sizes
2133         if (!aText.isEmpty())
2134         {
2135             Size aTmpSize( (aImageSize.Width()+8), (aImageSize.Height()+8) );
2136             if ( bTopImage )
2137             {
2138                 aImageRect.SetLeft( (rSize.Width()-aTmpSize.Width())/2 );
2139                 aImageRect.SetTop( (rSize.Height()-(aTmpSize.Height()+nTextHeight+6))/2 );
2140             }
2141             else
2142                 aImageRect.SetTop( (rSize.Height()-aTmpSize.Height())/2 );
2143 
2144             aImageRect.SetRight( aImageRect.Left()+aTmpSize.Width() );
2145             aImageRect.SetBottom( aImageRect.Top()+aTmpSize.Height() );
2146 
2147             // display text
2148             Point aTxtPos = rPos;
2149             if ( bTopImage )
2150             {
2151                 aTxtPos.AdjustX((rSize.Width()-nTextWidth)/2 );
2152                 aTxtPos.AdjustY(aImageRect.Bottom()+6 );
2153             }
2154             else
2155             {
2156                 aTxtPos.AdjustX(aImageRect.Right()+8 );
2157                 aTxtPos.AdjustY((rSize.Height()-nTextHeight)/2 );
2158             }
2159             pDev->DrawCtrlText( aTxtPos, aText, 0, aText.getLength() );
2160         }
2161 
2162         rMouseRect = aImageRect;
2163         rStateRect = aImageRect;
2164     }
2165 
2166     pDev->Pop();
2167 }
2168 
ImplDrawRadioButton(vcl::RenderContext & rRenderContext)2169 void RadioButton::ImplDrawRadioButton(vcl::RenderContext& rRenderContext)
2170 {
2171     HideFocus();
2172 
2173     Size aImageSize;
2174     if (!maImage)
2175         aImageSize = ImplGetRadioImageSize();
2176     else
2177         aImageSize  = maImage.GetSizePixel();
2178 
2179     aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
2180     aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
2181 
2182     // Draw control text
2183     ImplDraw(&rRenderContext, DrawFlags::NONE, Point(), GetOutputSizePixel(),
2184              aImageSize, maStateRect, maMouseRect);
2185 
2186     if (!maImage && HasFocus())
2187         ShowFocus(ImplGetFocusRect());
2188 
2189     ImplDrawRadioButtonState(rRenderContext);
2190 }
2191 
group(RadioButton & rOther)2192 void RadioButton::group(RadioButton &rOther)
2193 {
2194     if (&rOther == this)
2195         return;
2196 
2197     if (!m_xGroup)
2198     {
2199         m_xGroup = std::make_shared<std::vector<VclPtr<RadioButton> >>();
2200         m_xGroup->push_back(this);
2201     }
2202 
2203     auto aFind = std::find(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(&rOther));
2204     if (aFind == m_xGroup->end())
2205     {
2206         m_xGroup->push_back(&rOther);
2207 
2208         if (rOther.m_xGroup)
2209         {
2210             std::vector< VclPtr<RadioButton> > aOthers(rOther.GetRadioButtonGroup(false));
2211             //make all members of the group share the same button group
2212             for (auto const& elem : aOthers)
2213             {
2214                 aFind = std::find(m_xGroup->begin(), m_xGroup->end(), elem);
2215                 if (aFind == m_xGroup->end())
2216                     m_xGroup->push_back(elem);
2217             }
2218         }
2219 
2220         //make all members of the group share the same button group
2221         for (VclPtr<RadioButton> const & pButton : *m_xGroup)
2222         {
2223             pButton->m_xGroup = m_xGroup;
2224         }
2225     }
2226 
2227     //if this one is checked, uncheck all the others
2228     if (mbChecked)
2229         ImplUncheckAllOther();
2230 }
2231 
GetRadioButtonGroup(bool bIncludeThis) const2232 std::vector< VclPtr<RadioButton> > RadioButton::GetRadioButtonGroup(bool bIncludeThis) const
2233 {
2234     if (m_xGroup)
2235     {
2236         if (bIncludeThis)
2237             return *m_xGroup;
2238         std::vector< VclPtr<RadioButton> > aGroup;
2239         for (VclPtr<RadioButton> const & pRadioButton : *m_xGroup)
2240         {
2241             if (pRadioButton == this)
2242                 continue;
2243             aGroup.push_back(pRadioButton);
2244         }
2245         return aGroup;
2246     }
2247 
2248     std::vector<VclPtr<RadioButton>> aGroup;
2249     if (mbUsesExplicitGroup)
2250         return aGroup;
2251 
2252     //old-school
2253 
2254     // go back to first in group;
2255     vcl::Window* pFirst = const_cast<RadioButton*>(this);
2256     while( ( pFirst->GetStyle() & WB_GROUP ) == 0 )
2257     {
2258         vcl::Window* pWindow = pFirst->GetWindow( GetWindowType::Prev );
2259         if( pWindow )
2260             pFirst = pWindow;
2261         else
2262             break;
2263     }
2264     // insert radiobuttons up to next group
2265     do
2266     {
2267         if( pFirst->GetType() == WindowType::RADIOBUTTON )
2268         {
2269             if( pFirst != this || bIncludeThis )
2270                 aGroup.emplace_back(static_cast<RadioButton*>(pFirst) );
2271         }
2272         pFirst = pFirst->GetWindow( GetWindowType::Next );
2273     } while( pFirst && ( ( pFirst->GetStyle() & WB_GROUP ) == 0 ) );
2274 
2275     return aGroup;
2276 }
2277 
ImplUncheckAllOther()2278 void RadioButton::ImplUncheckAllOther()
2279 {
2280     mpWindowImpl->mnStyle |= WB_TABSTOP;
2281 
2282     std::vector<VclPtr<RadioButton> > aGroup(GetRadioButtonGroup(false));
2283     // iterate over radio button group and checked buttons
2284     for (VclPtr<RadioButton>& pWindow : aGroup)
2285     {
2286         if ( pWindow->IsChecked() )
2287         {
2288             pWindow->SetState( false );
2289             if ( pWindow->isDisposed() )
2290                 return;
2291         }
2292 
2293         // not inside if clause to always remove wrongly set WB_TABSTOPS
2294         pWindow->mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2295     }
2296 }
2297 
ImplCallClick(bool bGrabFocus,GetFocusFlags nFocusFlags)2298 void RadioButton::ImplCallClick( bool bGrabFocus, GetFocusFlags nFocusFlags )
2299 {
2300     mbStateChanged = !mbChecked;
2301     mbChecked = true;
2302     mpWindowImpl->mnStyle |= WB_TABSTOP;
2303     Invalidate();
2304     VclPtr<vcl::Window> xWindow = this;
2305     if ( mbRadioCheck )
2306         ImplUncheckAllOther();
2307     if ( xWindow->isDisposed() )
2308         return;
2309     if ( bGrabFocus )
2310         ImplGrabFocus( nFocusFlags );
2311     if ( xWindow->isDisposed() )
2312         return;
2313     if ( mbStateChanged )
2314         Toggle();
2315     if ( xWindow->isDisposed() )
2316         return;
2317     Click();
2318     if ( xWindow->isDisposed() )
2319         return;
2320     mbStateChanged = false;
2321 }
2322 
RadioButton(vcl::Window * pParent,bool bUsesExplicitGroup,WinBits nStyle)2323 RadioButton::RadioButton(vcl::Window* pParent, bool bUsesExplicitGroup, WinBits nStyle)
2324     : Button(WindowType::RADIOBUTTON)
2325     , mbUsesExplicitGroup(bUsesExplicitGroup)
2326 {
2327     ImplInitRadioButtonData();
2328     ImplInit( pParent, nStyle );
2329 }
2330 
~RadioButton()2331 RadioButton::~RadioButton()
2332 {
2333     disposeOnce();
2334 }
2335 
dispose()2336 void RadioButton::dispose()
2337 {
2338     if (m_xGroup)
2339     {
2340         m_xGroup->erase(std::remove(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(this)),
2341                         m_xGroup->end());
2342         m_xGroup.reset();
2343     }
2344     Button::dispose();
2345 }
2346 
MouseButtonDown(const MouseEvent & rMEvt)2347 void RadioButton::MouseButtonDown( const MouseEvent& rMEvt )
2348 {
2349     if ( rMEvt.IsLeft() && maMouseRect.IsInside( rMEvt.GetPosPixel() ) )
2350     {
2351         GetButtonState() |= DrawButtonFlags::Pressed;
2352         Invalidate();
2353         StartTracking();
2354         return;
2355     }
2356 
2357     Button::MouseButtonDown( rMEvt );
2358 }
2359 
Tracking(const TrackingEvent & rTEvt)2360 void RadioButton::Tracking( const TrackingEvent& rTEvt )
2361 {
2362     if ( rTEvt.IsTrackingEnded() )
2363     {
2364         if ( GetButtonState() & DrawButtonFlags::Pressed )
2365         {
2366             if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
2367                 GrabFocus();
2368 
2369             GetButtonState() &= ~DrawButtonFlags::Pressed;
2370 
2371             // do not call click handler if aborted
2372             if ( !rTEvt.IsTrackingCanceled() )
2373                 ImplCallClick();
2374             else
2375             {
2376                 Invalidate();
2377             }
2378         }
2379     }
2380     else
2381     {
2382         if ( maMouseRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() ) )
2383         {
2384             if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
2385             {
2386                 GetButtonState() |= DrawButtonFlags::Pressed;
2387                 Invalidate();
2388             }
2389         }
2390         else
2391         {
2392             if ( GetButtonState() & DrawButtonFlags::Pressed )
2393             {
2394                 GetButtonState() &= ~DrawButtonFlags::Pressed;
2395                 Invalidate();
2396             }
2397         }
2398     }
2399 }
2400 
KeyInput(const KeyEvent & rKEvt)2401 void RadioButton::KeyInput( const KeyEvent& rKEvt )
2402 {
2403     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
2404 
2405     if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
2406     {
2407         if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
2408         {
2409             GetButtonState() |= DrawButtonFlags::Pressed;
2410             Invalidate();
2411         }
2412     }
2413     else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
2414     {
2415         GetButtonState() &= ~DrawButtonFlags::Pressed;
2416         Invalidate();
2417     }
2418     else
2419         Button::KeyInput( rKEvt );
2420 }
2421 
KeyUp(const KeyEvent & rKEvt)2422 void RadioButton::KeyUp( const KeyEvent& rKEvt )
2423 {
2424     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
2425 
2426     if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
2427     {
2428         GetButtonState() &= ~DrawButtonFlags::Pressed;
2429         ImplCallClick();
2430     }
2431     else
2432         Button::KeyUp( rKEvt );
2433 }
2434 
FillLayoutData() const2435 void RadioButton::FillLayoutData() const
2436 {
2437     mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
2438     const_cast<RadioButton*>(this)->Invalidate();
2439 }
2440 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)2441 void RadioButton::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
2442 {
2443     ImplDrawRadioButton(rRenderContext);
2444 }
2445 
Draw(OutputDevice * pDev,const Point & rPos,DrawFlags nFlags)2446 void RadioButton::Draw( OutputDevice* pDev, const Point& rPos,
2447                         DrawFlags nFlags )
2448 {
2449     if ( !maImage )
2450     {
2451         MapMode     aResMapMode( MapUnit::Map100thMM );
2452         Point       aPos  = pDev->LogicToPixel( rPos );
2453         Size        aSize = GetSizePixel();
2454         Size        aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
2455         Size        aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
2456         Size        aBrd2Size = pDev->LogicToPixel( Size( 60, 60 ), aResMapMode );
2457         vcl::Font   aFont = GetDrawPixelFont( pDev );
2458         tools::Rectangle   aStateRect;
2459         tools::Rectangle   aMouseRect;
2460 
2461         aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
2462         aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
2463         aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
2464         aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
2465         aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
2466         aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
2467 
2468         if ( !aBrd1Size.Width() )
2469             aBrd1Size.setWidth( 1 );
2470         if ( !aBrd1Size.Height() )
2471             aBrd1Size.setHeight( 1 );
2472         if ( !aBrd2Size.Width() )
2473             aBrd2Size.setWidth( 1 );
2474         if ( !aBrd2Size.Height() )
2475             aBrd2Size.setHeight( 1 );
2476 
2477         pDev->Push();
2478         pDev->SetMapMode();
2479         pDev->SetFont( aFont );
2480         if ( nFlags & DrawFlags::Mono )
2481             pDev->SetTextColor( COL_BLACK );
2482         else
2483             pDev->SetTextColor( GetTextColor() );
2484         pDev->SetTextFillColor();
2485 
2486         ImplDraw( pDev, nFlags, aPos, aSize,
2487                   aImageSize, aStateRect, aMouseRect );
2488 
2489         Point   aCenterPos = aStateRect.Center();
2490         tools::Long    nRadX = aImageSize.Width()/2;
2491         tools::Long    nRadY = aImageSize.Height()/2;
2492 
2493         pDev->SetLineColor();
2494         pDev->SetFillColor( COL_BLACK );
2495         pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2496         nRadX -= aBrd1Size.Width();
2497         nRadY -= aBrd1Size.Height();
2498         pDev->SetFillColor( COL_WHITE );
2499         pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2500         if ( mbChecked )
2501         {
2502             nRadX -= aBrd1Size.Width();
2503             nRadY -= aBrd1Size.Height();
2504             if ( !nRadX )
2505                 nRadX = 1;
2506             if ( !nRadY )
2507                 nRadY = 1;
2508             pDev->SetFillColor( COL_BLACK );
2509             pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2510         }
2511 
2512         pDev->Pop();
2513     }
2514     else
2515     {
2516         OSL_FAIL( "RadioButton::Draw() - not implemented for RadioButton with Image" );
2517     }
2518 }
2519 
Resize()2520 void RadioButton::Resize()
2521 {
2522     Control::Resize();
2523     Invalidate();
2524 }
2525 
GetFocus()2526 void RadioButton::GetFocus()
2527 {
2528     ShowFocus( ImplGetFocusRect() );
2529     SetInputContext( InputContext( GetFont() ) );
2530     Button::GetFocus();
2531 }
2532 
LoseFocus()2533 void RadioButton::LoseFocus()
2534 {
2535     if ( GetButtonState() & DrawButtonFlags::Pressed )
2536     {
2537         GetButtonState() &= ~DrawButtonFlags::Pressed;
2538         Invalidate();
2539     }
2540 
2541     HideFocus();
2542     Button::LoseFocus();
2543 }
2544 
StateChanged(StateChangedType nType)2545 void RadioButton::StateChanged( StateChangedType nType )
2546 {
2547     Button::StateChanged( nType );
2548 
2549     if ( nType == StateChangedType::State )
2550     {
2551         if ( IsReallyVisible() && IsUpdateMode() )
2552             Invalidate( maStateRect );
2553     }
2554     else if ( (nType == StateChangedType::Enable) ||
2555               (nType == StateChangedType::Text) ||
2556               (nType == StateChangedType::Data) ||
2557               (nType == StateChangedType::UpdateMode) )
2558     {
2559         if ( IsUpdateMode() )
2560             Invalidate();
2561     }
2562     else if ( nType == StateChangedType::Style )
2563     {
2564         SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
2565 
2566         if ( (GetPrevStyle() & RADIOBUTTON_VIEW_STYLE) !=
2567              (GetStyle() & RADIOBUTTON_VIEW_STYLE) )
2568         {
2569             if ( IsUpdateMode() )
2570                 Invalidate();
2571         }
2572     }
2573     else if ( (nType == StateChangedType::Zoom) ||
2574               (nType == StateChangedType::ControlFont) )
2575     {
2576         ImplInitSettings( false );
2577         Invalidate();
2578     }
2579     else if ( nType == StateChangedType::ControlForeground )
2580     {
2581         ImplInitSettings( false );
2582         Invalidate();
2583     }
2584     else if ( nType == StateChangedType::ControlBackground )
2585     {
2586         ImplInitSettings( true );
2587         Invalidate();
2588     }
2589 }
2590 
DataChanged(const DataChangedEvent & rDCEvt)2591 void RadioButton::DataChanged( const DataChangedEvent& rDCEvt )
2592 {
2593     Button::DataChanged( rDCEvt );
2594 
2595     if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2596          (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2597          ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2598           (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2599     {
2600         ImplInitSettings( true );
2601         Invalidate();
2602     }
2603 }
2604 
PreNotify(NotifyEvent & rNEvt)2605 bool RadioButton::PreNotify( NotifyEvent& rNEvt )
2606 {
2607     if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
2608     {
2609         const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
2610         if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
2611         {
2612             // trigger redraw if mouse over state has changed
2613             if( IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire) )
2614             {
2615                 if (maMouseRect.IsInside(GetPointerPosPixel()) != maMouseRect.IsInside(GetLastPointerPosPixel()) ||
2616                     pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
2617                 {
2618                     Invalidate( maStateRect );
2619                 }
2620             }
2621         }
2622     }
2623 
2624     return Button::PreNotify(rNEvt);
2625 }
2626 
Toggle()2627 void RadioButton::Toggle()
2628 {
2629     ImplCallEventListenersAndHandler( VclEventId::RadiobuttonToggle, [this] () { maToggleHdl.Call(*this); } );
2630 }
2631 
SetModeRadioImage(const Image & rImage)2632 void RadioButton::SetModeRadioImage( const Image& rImage )
2633 {
2634     if ( rImage != maImage )
2635     {
2636         maImage = rImage;
2637         CompatStateChanged( StateChangedType::Data );
2638         queue_resize();
2639     }
2640 }
2641 
2642 
SetState(bool bCheck)2643 void RadioButton::SetState( bool bCheck )
2644 {
2645     // carry the TabStop flag along correctly
2646     if ( bCheck )
2647         mpWindowImpl->mnStyle |= WB_TABSTOP;
2648     else
2649         mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2650 
2651     if ( mbChecked != bCheck )
2652     {
2653         mbChecked = bCheck;
2654         CompatStateChanged( StateChangedType::State );
2655         Toggle();
2656     }
2657 }
2658 
set_property(const OString & rKey,const OUString & rValue)2659 bool RadioButton::set_property(const OString &rKey, const OUString &rValue)
2660 {
2661     if (rKey == "active")
2662         SetState(toBool(rValue));
2663     else if (rKey == "image-position")
2664     {
2665         WinBits nBits = GetStyle();
2666         if (rValue == "left")
2667         {
2668             nBits &= ~(WB_CENTER | WB_RIGHT);
2669             nBits |= WB_LEFT;
2670         }
2671         else if (rValue == "right")
2672         {
2673             nBits &= ~(WB_CENTER | WB_LEFT);
2674             nBits |= WB_RIGHT;
2675         }
2676         else if (rValue == "top")
2677         {
2678             nBits &= ~(WB_VCENTER | WB_BOTTOM);
2679             nBits |= WB_TOP;
2680         }
2681         else if (rValue == "bottom")
2682         {
2683             nBits &= ~(WB_VCENTER | WB_TOP);
2684             nBits |= WB_BOTTOM;
2685         }
2686         //It's rather mad to have to set these bits when there is the other
2687         //image align. Looks like e.g. the radiobuttons etc weren't converted
2688         //over to image align fully.
2689         SetStyle(nBits);
2690         //Deliberate to set the sane ImageAlign property
2691         return Button::set_property(rKey, rValue);
2692     }
2693     else
2694         return Button::set_property(rKey, rValue);
2695     return true;
2696 }
2697 
Check(bool bCheck)2698 void RadioButton::Check( bool bCheck )
2699 {
2700     // TabStop-Flag richtig mitfuehren
2701     if ( bCheck )
2702         mpWindowImpl->mnStyle |= WB_TABSTOP;
2703     else
2704         mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2705 
2706     if ( mbChecked == bCheck )
2707         return;
2708 
2709     mbChecked = bCheck;
2710     VclPtr<vcl::Window> xWindow = this;
2711     CompatStateChanged( StateChangedType::State );
2712     if ( xWindow->isDisposed() )
2713         return;
2714     if ( bCheck && mbRadioCheck )
2715         ImplUncheckAllOther();
2716     if ( xWindow->isDisposed() )
2717         return;
2718     Toggle();
2719 }
2720 
ImplGetImageToTextDistance() const2721 tools::Long Button::ImplGetImageToTextDistance() const
2722 {
2723     // 4 pixels, but take zoom into account, so the text doesn't "jump" relative to surrounding elements,
2724     // which might have been aligned with the text of the check box
2725     return CalcZoom( 4 );
2726 }
2727 
ImplGetRadioImageSize() const2728 Size RadioButton::ImplGetRadioImageSize() const
2729 {
2730     Size aSize;
2731     bool bDefaultSize = true;
2732     if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
2733     {
2734         ImplControlValue aControlValue;
2735         tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
2736         tools::Rectangle aBoundingRgn, aContentRgn;
2737 
2738         // get native size of a radio button
2739         if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
2740                                     ControlState::DEFAULT|ControlState::ENABLED,
2741                                     aControlValue,
2742                                     aBoundingRgn, aContentRgn ) )
2743         {
2744             aSize = aContentRgn.GetSize();
2745             bDefaultSize = false;
2746         }
2747     }
2748     if( bDefaultSize )
2749         aSize = GetRadioImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
2750     return aSize;
2751 }
2752 
LoadThemedImageList(const StyleSettings & rStyleSettings,std::vector<Image> & rList,const std::vector<OUString> & rResources)2753 static void LoadThemedImageList(const StyleSettings &rStyleSettings,
2754                                 std::vector<Image>& rList, const std::vector<OUString> &rResources)
2755 {
2756     Color aColorAry1[6];
2757     Color aColorAry2[6];
2758     aColorAry1[0] = Color( 0xC0, 0xC0, 0xC0 );
2759     aColorAry1[1] = Color( 0xFF, 0xFF, 0x00 );
2760     aColorAry1[2] = Color( 0xFF, 0xFF, 0xFF );
2761     aColorAry1[3] = Color( 0x80, 0x80, 0x80 );
2762     aColorAry1[4] = Color( 0x00, 0x00, 0x00 );
2763     aColorAry1[5] = Color( 0x00, 0xFF, 0x00 );
2764     aColorAry2[0] = rStyleSettings.GetFaceColor();
2765     aColorAry2[1] = rStyleSettings.GetWindowColor();
2766     aColorAry2[2] = rStyleSettings.GetLightColor();
2767     aColorAry2[3] = rStyleSettings.GetShadowColor();
2768     aColorAry2[4] = rStyleSettings.GetDarkShadowColor();
2769     aColorAry2[5] = rStyleSettings.GetWindowTextColor();
2770 
2771     static_assert( sizeof(aColorAry1) == sizeof(aColorAry2), "aColorAry1 must match aColorAry2" );
2772 
2773     for (const auto &a : rResources)
2774     {
2775         BitmapEx aBmpEx(a);
2776         aBmpEx.Replace(aColorAry1, aColorAry2, SAL_N_ELEMENTS(aColorAry1));
2777         rList.emplace_back(aBmpEx);
2778     }
2779 }
2780 
GetRadioImage(const AllSettings & rSettings,DrawButtonFlags nFlags)2781 Image RadioButton::GetRadioImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
2782 {
2783     ImplSVData*             pSVData = ImplGetSVData();
2784     const StyleSettings&    rStyleSettings = rSettings.GetStyleSettings();
2785     sal_uInt16              nStyle = 0;
2786 
2787     if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
2788         nStyle = STYLE_RADIOBUTTON_MONO;
2789 
2790     if ( pSVData->maCtrlData.maRadioImgList.empty() ||
2791          (pSVData->maCtrlData.mnRadioStyle != nStyle) ||
2792          (pSVData->maCtrlData.mnLastRadioFColor != rStyleSettings.GetFaceColor()) ||
2793          (pSVData->maCtrlData.mnLastRadioWColor != rStyleSettings.GetWindowColor()) ||
2794          (pSVData->maCtrlData.mnLastRadioLColor != rStyleSettings.GetLightColor()) )
2795     {
2796         pSVData->maCtrlData.maRadioImgList.clear();
2797 
2798         pSVData->maCtrlData.mnLastRadioFColor = rStyleSettings.GetFaceColor();
2799         pSVData->maCtrlData.mnLastRadioWColor = rStyleSettings.GetWindowColor();
2800         pSVData->maCtrlData.mnLastRadioLColor = rStyleSettings.GetLightColor();
2801 
2802         std::vector<OUString> aResources;
2803         if (nStyle)
2804         {
2805             aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO1);
2806             aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO2);
2807             aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO3);
2808             aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO4);
2809             aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO5);
2810             aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO6);
2811         }
2812         else
2813         {
2814             aResources.emplace_back(SV_RESID_BITMAP_RADIO1);
2815             aResources.emplace_back(SV_RESID_BITMAP_RADIO2);
2816             aResources.emplace_back(SV_RESID_BITMAP_RADIO3);
2817             aResources.emplace_back(SV_RESID_BITMAP_RADIO4);
2818             aResources.emplace_back(SV_RESID_BITMAP_RADIO5);
2819             aResources.emplace_back(SV_RESID_BITMAP_RADIO6);
2820         }
2821         LoadThemedImageList( rStyleSettings, pSVData->maCtrlData.maRadioImgList, aResources);
2822         pSVData->maCtrlData.mnRadioStyle = nStyle;
2823     }
2824 
2825     sal_uInt16 nIndex;
2826     if ( nFlags & DrawButtonFlags::Disabled )
2827     {
2828         if ( nFlags & DrawButtonFlags::Checked )
2829             nIndex = 5;
2830         else
2831             nIndex = 4;
2832     }
2833     else if ( nFlags & DrawButtonFlags::Pressed )
2834     {
2835         if ( nFlags & DrawButtonFlags::Checked )
2836             nIndex = 3;
2837         else
2838             nIndex = 2;
2839     }
2840     else
2841     {
2842         if ( nFlags & DrawButtonFlags::Checked )
2843             nIndex = 1;
2844         else
2845             nIndex = 0;
2846     }
2847     return pSVData->maCtrlData.maRadioImgList[nIndex];
2848 }
2849 
ImplAdjustNWFSizes()2850 void RadioButton::ImplAdjustNWFSizes()
2851 {
2852     GetOutDev()->Push( PushFlags::MAPMODE );
2853     SetMapMode(MapMode(MapUnit::MapPixel));
2854 
2855     ImplControlValue aControlValue;
2856     Size aCurSize( GetSizePixel() );
2857     tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
2858     tools::Rectangle aBoundingRgn, aContentRgn;
2859 
2860     // get native size of a radiobutton
2861     if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
2862                                 ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
2863                                 aBoundingRgn, aContentRgn ) )
2864     {
2865         Size aSize = aContentRgn.GetSize();
2866 
2867         if( aSize.Height() > aCurSize.Height() )
2868         {
2869             aCurSize.setHeight( aSize.Height() );
2870             SetSizePixel( aCurSize );
2871         }
2872     }
2873 
2874     GetOutDev()->Pop();
2875 }
2876 
CalcMinimumSize(tools::Long nMaxWidth) const2877 Size RadioButton::CalcMinimumSize(tools::Long nMaxWidth) const
2878 {
2879     Size aSize;
2880     if ( !maImage )
2881         aSize = ImplGetRadioImageSize();
2882     else
2883     {
2884         aSize = maImage.GetSizePixel();
2885         aSize.AdjustWidth(8);
2886         aSize.AdjustHeight(8);
2887     }
2888 
2889     if (Button::HasImage())
2890     {
2891         Size aImgSize = GetModeImage().GetSizePixel();
2892         aSize = Size(std::max(aImgSize.Width(), aSize.Width()),
2893                      std::max(aImgSize.Height(), aSize.Height()));
2894     }
2895 
2896     OUString aText = GetText();
2897     if (!aText.isEmpty())
2898     {
2899         bool bTopImage = (GetStyle() & WB_TOP) != 0;
2900 
2901         Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
2902                                       aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
2903 
2904         aSize.AdjustWidth(2 );   // for focus rect
2905 
2906         if (!bTopImage)
2907         {
2908             aSize.AdjustWidth(ImplGetImageToTextDistance() );
2909             aSize.AdjustWidth(aTextSize.Width() );
2910             if ( aSize.Height() < aTextSize.Height() )
2911                 aSize.setHeight( aTextSize.Height() );
2912         }
2913         else
2914         {
2915             aSize.AdjustHeight(6 );
2916             aSize.AdjustHeight(GetTextHeight() );
2917             if ( aSize.Width() < aTextSize.Width() )
2918                 aSize.setWidth( aTextSize.Width() );
2919         }
2920     }
2921 
2922     return CalcWindowSize( aSize );
2923 }
2924 
GetOptimalSize() const2925 Size RadioButton::GetOptimalSize() const
2926 {
2927     return CalcMinimumSize();
2928 }
2929 
ShowFocus(const tools::Rectangle & rRect)2930 void RadioButton::ShowFocus(const tools::Rectangle& rRect)
2931 {
2932     if (IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Focus))
2933     {
2934         ImplControlValue aControlValue;
2935         tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
2936 
2937         aInRect.SetLeft( rRect.Left() );  // exclude the radio element itself from the focusrect
2938 
2939         GetOutDev()->DrawNativeControl(ControlType::Radiobutton, ControlPart::Focus, aInRect,
2940                           ControlState::FOCUSED, aControlValue, OUString());
2941     }
2942     Button::ShowFocus(rRect);
2943 }
2944 
DumpAsPropertyTree(tools::JsonWriter & rJsonWriter)2945 void RadioButton::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
2946 {
2947     Button::DumpAsPropertyTree(rJsonWriter);
2948     rJsonWriter.put("checked", IsChecked());
2949 
2950     OUString sGroupId;
2951     std::vector<VclPtr<RadioButton>> aGroup = GetRadioButtonGroup();
2952     for(auto& pButton : aGroup)
2953         sGroupId += pButton->get_id();
2954 
2955     if (!sGroupId.isEmpty())
2956         rJsonWriter.put("group", sGroupId);
2957 }
2958 
GetUITestFactory() const2959 FactoryFunction RadioButton::GetUITestFactory() const
2960 {
2961     return RadioButtonUIObject::create;
2962 }
2963 
ImplInitCheckBoxData()2964 void CheckBox::ImplInitCheckBoxData()
2965 {
2966     meState         = TRISTATE_FALSE;
2967     mbTriState      = false;
2968 }
2969 
ImplInit(vcl::Window * pParent,WinBits nStyle)2970 void CheckBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
2971 {
2972     nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
2973     Button::ImplInit( pParent, nStyle, nullptr );
2974 
2975     ImplInitSettings( true );
2976 }
2977 
ImplInitStyle(const vcl::Window * pPrevWindow,WinBits nStyle)2978 WinBits CheckBox::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
2979 {
2980     if ( !(nStyle & WB_NOTABSTOP) )
2981         nStyle |= WB_TABSTOP;
2982     if ( !(nStyle & WB_NOGROUP) &&
2983          (!pPrevWindow || (pPrevWindow->GetType() != WindowType::CHECKBOX)) )
2984         nStyle |= WB_GROUP;
2985     return nStyle;
2986 }
2987 
GetCanonicalFont(const StyleSettings & _rStyle) const2988 const vcl::Font& CheckBox::GetCanonicalFont( const StyleSettings& _rStyle ) const
2989 {
2990     return _rStyle.GetRadioCheckFont();
2991 }
2992 
GetCanonicalTextColor(const StyleSettings & _rStyle) const2993 const Color& CheckBox::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
2994 {
2995     return _rStyle.GetRadioCheckTextColor();
2996 }
2997 
ImplInitSettings(bool bBackground)2998 void CheckBox::ImplInitSettings( bool bBackground )
2999 {
3000     Button::ImplInitSettings();
3001 
3002     if ( !bBackground )
3003         return;
3004 
3005     vcl::Window* pParent = GetParent();
3006     if ( !IsControlBackground() &&
3007         (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) ) )
3008     {
3009         EnableChildTransparentMode();
3010         SetParentClipMode( ParentClipMode::NoClip );
3011         SetPaintTransparent( true );
3012         SetBackground();
3013         if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
3014             ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
3015     }
3016     else
3017     {
3018         EnableChildTransparentMode( false );
3019         SetParentClipMode();
3020         SetPaintTransparent( false );
3021 
3022         if ( IsControlBackground() )
3023             SetBackground( GetControlBackground() );
3024         else
3025             SetBackground( pParent->GetBackground() );
3026     }
3027 }
3028 
ImplDrawCheckBoxState(vcl::RenderContext & rRenderContext)3029 void CheckBox::ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext)
3030 {
3031     bool bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire);
3032     if (bNativeOK)
3033     {
3034         ImplControlValue aControlValue(meState == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
3035         tools::Rectangle aCtrlRegion(maStateRect);
3036         ControlState nState = ControlState::NONE;
3037 
3038         if (HasFocus())
3039             nState |= ControlState::FOCUSED;
3040         if (GetButtonState() & DrawButtonFlags::Default)
3041             nState |= ControlState::DEFAULT;
3042         if (GetButtonState() & DrawButtonFlags::Pressed)
3043             nState |= ControlState::PRESSED;
3044         if (IsEnabled())
3045             nState |= ControlState::ENABLED;
3046 
3047         if (meState == TRISTATE_TRUE)
3048             aControlValue.setTristateVal(ButtonValue::On);
3049         else if (meState == TRISTATE_INDET)
3050             aControlValue.setTristateVal(ButtonValue::Mixed);
3051 
3052         if (IsMouseOver() && maMouseRect.IsInside(GetPointerPosPixel()))
3053             nState |= ControlState::ROLLOVER;
3054 
3055         bNativeOK = rRenderContext.DrawNativeControl(ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3056                                                      nState, aControlValue, OUString());
3057     }
3058 
3059     if (bNativeOK)
3060         return;
3061 
3062     DrawButtonFlags nStyle = GetButtonState();
3063     if (!IsEnabled())
3064         nStyle |= DrawButtonFlags::Disabled;
3065     if (meState == TRISTATE_INDET)
3066         nStyle |= DrawButtonFlags::DontKnow;
3067     else if (meState == TRISTATE_TRUE)
3068         nStyle |= DrawButtonFlags::Checked;
3069     Image aImage = GetCheckImage(GetSettings(), nStyle);
3070     if (IsZoom())
3071         rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
3072     else
3073         rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
3074 }
3075 
ImplDraw(OutputDevice * pDev,DrawFlags nDrawFlags,const Point & rPos,const Size & rSize,const Size & rImageSize,tools::Rectangle & rStateRect,tools::Rectangle & rMouseRect)3076 void CheckBox::ImplDraw( OutputDevice* pDev, DrawFlags nDrawFlags,
3077                          const Point& rPos, const Size& rSize,
3078                          const Size& rImageSize, tools::Rectangle& rStateRect,
3079                          tools::Rectangle& rMouseRect )
3080 {
3081     WinBits                 nWinStyle = GetStyle();
3082     OUString                aText( GetText() );
3083 
3084     pDev->Push( PushFlags::CLIPREGION | PushFlags::LINECOLOR );
3085     pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
3086 
3087     if (!aText.isEmpty() || HasImage())
3088     {
3089         Button::ImplDrawRadioCheck(pDev, nWinStyle, nDrawFlags,
3090                                    rPos, rSize, rImageSize,
3091                                    rStateRect, rMouseRect);
3092     }
3093     else
3094     {
3095         rStateRect.SetLeft( rPos.X() );
3096         if ( nWinStyle & WB_VCENTER )
3097             rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
3098         else if ( nWinStyle & WB_BOTTOM )
3099             rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() );
3100         else
3101             rStateRect.SetTop( rPos.Y() );
3102         rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
3103         rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
3104         // provide space for focusrect
3105         // note: this assumes that the control's size was adjusted
3106         // accordingly in Get/LoseFocus, so the onscreen position won't change
3107         if( HasFocus() )
3108             rStateRect.Move( 1, 1 );
3109         rMouseRect          = rStateRect;
3110 
3111         ImplSetFocusRect( rStateRect );
3112     }
3113 
3114     pDev->Pop();
3115 }
3116 
ImplDrawCheckBox(vcl::RenderContext & rRenderContext)3117 void CheckBox::ImplDrawCheckBox(vcl::RenderContext& rRenderContext)
3118 {
3119     Size aImageSize = ImplGetCheckImageSize();
3120     aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
3121     aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
3122 
3123     HideFocus();
3124 
3125     ImplDraw(&rRenderContext, DrawFlags::NONE, Point(), GetOutputSizePixel(),
3126              aImageSize, maStateRect, maMouseRect);
3127 
3128     ImplDrawCheckBoxState(rRenderContext);
3129     if (HasFocus())
3130         ShowFocus(ImplGetFocusRect());
3131 }
3132 
ImplCheck()3133 void CheckBox::ImplCheck()
3134 {
3135     TriState eNewState;
3136     if ( meState == TRISTATE_FALSE )
3137         eNewState = TRISTATE_TRUE;
3138     else if ( !mbTriState )
3139         eNewState = TRISTATE_FALSE;
3140     else if ( meState == TRISTATE_TRUE )
3141         eNewState = TRISTATE_INDET;
3142     else
3143         eNewState = TRISTATE_FALSE;
3144     meState = eNewState;
3145 
3146     VclPtr<vcl::Window> xWindow = this;
3147     Invalidate();
3148     Toggle();
3149     if ( xWindow->isDisposed() )
3150         return;
3151     Click();
3152 }
3153 
CheckBox(vcl::Window * pParent,WinBits nStyle)3154 CheckBox::CheckBox( vcl::Window* pParent, WinBits nStyle ) :
3155     Button( WindowType::CHECKBOX )
3156 {
3157     ImplInitCheckBoxData();
3158     ImplInit( pParent, nStyle );
3159 }
3160 
MouseButtonDown(const MouseEvent & rMEvt)3161 void CheckBox::MouseButtonDown( const MouseEvent& rMEvt )
3162 {
3163     if ( rMEvt.IsLeft() && maMouseRect.IsInside( rMEvt.GetPosPixel() ) )
3164     {
3165         GetButtonState() |= DrawButtonFlags::Pressed;
3166         Invalidate();
3167         StartTracking();
3168         return;
3169     }
3170 
3171     Button::MouseButtonDown( rMEvt );
3172 }
3173 
Tracking(const TrackingEvent & rTEvt)3174 void CheckBox::Tracking( const TrackingEvent& rTEvt )
3175 {
3176     if ( rTEvt.IsTrackingEnded() )
3177     {
3178         if ( GetButtonState() & DrawButtonFlags::Pressed )
3179         {
3180             if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
3181                 GrabFocus();
3182 
3183             GetButtonState() &= ~DrawButtonFlags::Pressed;
3184 
3185             // do not call click handler if aborted
3186             if ( !rTEvt.IsTrackingCanceled() )
3187                 ImplCheck();
3188             else
3189             {
3190                 Invalidate();
3191             }
3192         }
3193     }
3194     else
3195     {
3196         if ( maMouseRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() ) )
3197         {
3198             if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
3199             {
3200                 GetButtonState() |= DrawButtonFlags::Pressed;
3201                 Invalidate();
3202             }
3203         }
3204         else
3205         {
3206             if ( GetButtonState() & DrawButtonFlags::Pressed )
3207             {
3208                 GetButtonState() &= ~DrawButtonFlags::Pressed;
3209                 Invalidate();
3210             }
3211         }
3212     }
3213 }
3214 
KeyInput(const KeyEvent & rKEvt)3215 void CheckBox::KeyInput( const KeyEvent& rKEvt )
3216 {
3217     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3218 
3219     if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
3220     {
3221         if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
3222         {
3223             GetButtonState() |= DrawButtonFlags::Pressed;
3224             Invalidate();
3225         }
3226     }
3227     else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
3228     {
3229         GetButtonState() &= ~DrawButtonFlags::Pressed;
3230         Invalidate();
3231     }
3232     else
3233         Button::KeyInput( rKEvt );
3234 }
3235 
KeyUp(const KeyEvent & rKEvt)3236 void CheckBox::KeyUp( const KeyEvent& rKEvt )
3237 {
3238     vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3239 
3240     if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
3241     {
3242         GetButtonState() &= ~DrawButtonFlags::Pressed;
3243         ImplCheck();
3244     }
3245     else
3246         Button::KeyUp( rKEvt );
3247 }
3248 
FillLayoutData() const3249 void CheckBox::FillLayoutData() const
3250 {
3251     mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
3252     const_cast<CheckBox*>(this)->Invalidate();
3253 }
3254 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)3255 void CheckBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
3256 {
3257     ImplDrawCheckBox(rRenderContext);
3258 }
3259 
Draw(OutputDevice * pDev,const Point & rPos,DrawFlags nFlags)3260 void CheckBox::Draw( OutputDevice* pDev, const Point& rPos,
3261                      DrawFlags nFlags )
3262 {
3263     MapMode     aResMapMode( MapUnit::Map100thMM );
3264     Point       aPos  = pDev->LogicToPixel( rPos );
3265     Size        aSize = GetSizePixel();
3266     Size        aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
3267     Size        aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
3268     Size        aBrd2Size = pDev->LogicToPixel( Size( 30, 30 ), aResMapMode );
3269     tools::Long        nCheckWidth = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode ).Width();
3270     vcl::Font   aFont = GetDrawPixelFont( pDev );
3271     tools::Rectangle   aStateRect;
3272     tools::Rectangle   aMouseRect;
3273 
3274     aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
3275     aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
3276     aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
3277     aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
3278     aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
3279     aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
3280 
3281     if ( !aBrd1Size.Width() )
3282         aBrd1Size.setWidth( 1 );
3283     if ( !aBrd1Size.Height() )
3284         aBrd1Size.setHeight( 1 );
3285     if ( !aBrd2Size.Width() )
3286         aBrd2Size.setWidth( 1 );
3287     if ( !aBrd2Size.Height() )
3288         aBrd2Size.setHeight( 1 );
3289     if ( !nCheckWidth )
3290         nCheckWidth = 1;
3291 
3292     pDev->Push();
3293     pDev->SetMapMode();
3294     pDev->SetFont( aFont );
3295     if ( nFlags & DrawFlags::Mono )
3296         pDev->SetTextColor( COL_BLACK );
3297     else
3298         pDev->SetTextColor( GetTextColor() );
3299     pDev->SetTextFillColor();
3300 
3301     ImplDraw( pDev, nFlags, aPos, aSize,
3302               aImageSize, aStateRect, aMouseRect );
3303 
3304     pDev->SetLineColor();
3305     pDev->SetFillColor( COL_BLACK );
3306     pDev->DrawRect( aStateRect );
3307     aStateRect.AdjustLeft(aBrd1Size.Width() );
3308     aStateRect.AdjustTop(aBrd1Size.Height() );
3309     aStateRect.AdjustRight( -(aBrd1Size.Width()) );
3310     aStateRect.AdjustBottom( -(aBrd1Size.Height()) );
3311     if ( meState == TRISTATE_INDET )
3312         pDev->SetFillColor( COL_LIGHTGRAY );
3313     else
3314         pDev->SetFillColor( COL_WHITE );
3315     pDev->DrawRect( aStateRect );
3316 
3317     if ( meState == TRISTATE_TRUE )
3318     {
3319         aStateRect.AdjustLeft(aBrd2Size.Width() );
3320         aStateRect.AdjustTop(aBrd2Size.Height() );
3321         aStateRect.AdjustRight( -(aBrd2Size.Width()) );
3322         aStateRect.AdjustBottom( -(aBrd2Size.Height()) );
3323         Point   aPos11( aStateRect.TopLeft() );
3324         Point   aPos12( aStateRect.BottomRight() );
3325         Point   aPos21( aStateRect.TopRight() );
3326         Point   aPos22( aStateRect.BottomLeft() );
3327         Point   aTempPos11( aPos11 );
3328         Point   aTempPos12( aPos12 );
3329         Point   aTempPos21( aPos21 );
3330         Point   aTempPos22( aPos22 );
3331         pDev->SetLineColor( COL_BLACK );
3332         tools::Long nDX = 0;
3333         for ( tools::Long i = 0; i < nCheckWidth; i++ )
3334         {
3335             if ( !(i % 2) )
3336             {
3337                 aTempPos11.setX( aPos11.X()+nDX );
3338                 aTempPos12.setX( aPos12.X()+nDX );
3339                 aTempPos21.setX( aPos21.X()+nDX );
3340                 aTempPos22.setX( aPos22.X()+nDX );
3341             }
3342             else
3343             {
3344                 nDX++;
3345                 aTempPos11.setX( aPos11.X()-nDX );
3346                 aTempPos12.setX( aPos12.X()-nDX );
3347                 aTempPos21.setX( aPos21.X()-nDX );
3348                 aTempPos22.setX( aPos22.X()-nDX );
3349             }
3350             pDev->DrawLine( aTempPos11, aTempPos12 );
3351             pDev->DrawLine( aTempPos21, aTempPos22 );
3352         }
3353     }
3354 
3355     pDev->Pop();
3356 }
3357 
Resize()3358 void CheckBox::Resize()
3359 {
3360     Control::Resize();
3361     Invalidate();
3362 }
3363 
GetFocus()3364 void CheckBox::GetFocus()
3365 {
3366     if (GetText().isEmpty())
3367     {
3368         // increase button size to have space for focus rect
3369         // checkboxes without text will draw focusrect around the check
3370         // See CheckBox::ImplDraw()
3371         Point aPos( GetPosPixel() );
3372         Size aSize( GetSizePixel() );
3373         aPos.Move(-1,-1);
3374         aSize.AdjustHeight(2 );
3375         aSize.AdjustWidth(2 );
3376         setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
3377         Invalidate();
3378         // Trigger drawing to initialize the mouse rectangle, otherwise the mouse button down
3379         // handler would ignore the mouse event.
3380         PaintImmediately();
3381     }
3382     else
3383         ShowFocus( ImplGetFocusRect() );
3384 
3385     SetInputContext( InputContext( GetFont() ) );
3386     Button::GetFocus();
3387 }
3388 
LoseFocus()3389 void CheckBox::LoseFocus()
3390 {
3391     if ( GetButtonState() & DrawButtonFlags::Pressed )
3392     {
3393         GetButtonState() &= ~DrawButtonFlags::Pressed;
3394         Invalidate();
3395     }
3396 
3397     HideFocus();
3398     Button::LoseFocus();
3399 
3400     if (GetText().isEmpty())
3401     {
3402         // decrease button size again (see GetFocus())
3403         // checkboxes without text will draw focusrect around the check
3404         Point aPos( GetPosPixel() );
3405         Size aSize( GetSizePixel() );
3406         aPos.Move(1,1);
3407         aSize.AdjustHeight( -2 );
3408         aSize.AdjustWidth( -2 );
3409         setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
3410         Invalidate();
3411     }
3412 }
3413 
StateChanged(StateChangedType nType)3414 void CheckBox::StateChanged( StateChangedType nType )
3415 {
3416     Button::StateChanged( nType );
3417 
3418     if ( nType == StateChangedType::State )
3419     {
3420         if ( IsReallyVisible() && IsUpdateMode() )
3421             Invalidate( maStateRect );
3422     }
3423     else if ( (nType == StateChangedType::Enable) ||
3424               (nType == StateChangedType::Text) ||
3425               (nType == StateChangedType::Data) ||
3426               (nType == StateChangedType::UpdateMode) )
3427     {
3428         if ( IsUpdateMode() )
3429             Invalidate();
3430     }
3431     else if ( nType == StateChangedType::Style )
3432     {
3433         SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
3434 
3435         if ( (GetPrevStyle() & CHECKBOX_VIEW_STYLE) !=
3436              (GetStyle() & CHECKBOX_VIEW_STYLE) )
3437         {
3438             if ( IsUpdateMode() )
3439                 Invalidate();
3440         }
3441     }
3442     else if ( (nType == StateChangedType::Zoom) ||
3443               (nType == StateChangedType::ControlFont) )
3444     {
3445         ImplInitSettings( false );
3446         Invalidate();
3447     }
3448     else if ( nType == StateChangedType::ControlForeground )
3449     {
3450         ImplInitSettings( false );
3451         Invalidate();
3452     }
3453     else if ( nType == StateChangedType::ControlBackground )
3454     {
3455         ImplInitSettings( true );
3456         Invalidate();
3457     }
3458 }
3459 
DataChanged(const DataChangedEvent & rDCEvt)3460 void CheckBox::DataChanged( const DataChangedEvent& rDCEvt )
3461 {
3462     Button::DataChanged( rDCEvt );
3463 
3464     if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
3465          (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
3466          ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
3467           (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
3468     {
3469         ImplInitSettings( true );
3470         Invalidate();
3471     }
3472 }
3473 
PreNotify(NotifyEvent & rNEvt)3474 bool CheckBox::PreNotify( NotifyEvent& rNEvt )
3475 {
3476     if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
3477     {
3478         const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
3479         if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
3480         {
3481             // trigger redraw if mouse over state has changed
3482             if( IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire) )
3483             {
3484                 if (maMouseRect.IsInside(GetPointerPosPixel()) != maMouseRect.IsInside(GetLastPointerPosPixel()) ||
3485                     pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
3486                 {
3487                     Invalidate( maStateRect );
3488                 }
3489             }
3490         }
3491     }
3492 
3493     return Button::PreNotify(rNEvt);
3494 }
3495 
Toggle()3496 void CheckBox::Toggle()
3497 {
3498     ImplCallEventListenersAndHandler( VclEventId::CheckboxToggle, [this] () { maToggleHdl.Call(*this); } );
3499 }
3500 
SetState(TriState eState)3501 void CheckBox::SetState( TriState eState )
3502 {
3503     if ( !mbTriState && (eState == TRISTATE_INDET) )
3504         eState = TRISTATE_FALSE;
3505 
3506     if ( meState != eState )
3507     {
3508         meState = eState;
3509         StateChanged( StateChangedType::State );
3510         Toggle();
3511     }
3512 }
3513 
set_property(const OString & rKey,const OUString & rValue)3514 bool CheckBox::set_property(const OString &rKey, const OUString &rValue)
3515 {
3516     if (rKey == "active")
3517         SetState(toBool(rValue) ? TRISTATE_TRUE : TRISTATE_FALSE);
3518     else
3519         return Button::set_property(rKey, rValue);
3520     return true;
3521 }
3522 
EnableTriState(bool bTriState)3523 void CheckBox::EnableTriState( bool bTriState )
3524 {
3525     if ( mbTriState != bTriState )
3526     {
3527         mbTriState = bTriState;
3528 
3529         if ( !bTriState && (meState == TRISTATE_INDET) )
3530             SetState( TRISTATE_FALSE );
3531     }
3532 }
3533 
ImplGetCheckImageSize() const3534 Size CheckBox::ImplGetCheckImageSize() const
3535 {
3536     Size aSize;
3537     bool bDefaultSize = true;
3538     if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
3539     {
3540         ImplControlValue aControlValue;
3541         tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
3542         tools::Rectangle aBoundingRgn, aContentRgn;
3543 
3544         // get native size of a check box
3545         if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3546                                     ControlState::DEFAULT|ControlState::ENABLED,
3547                                     aControlValue,
3548                                     aBoundingRgn, aContentRgn ) )
3549         {
3550             aSize = aContentRgn.GetSize();
3551             bDefaultSize = false;
3552         }
3553     }
3554     if( bDefaultSize )
3555         aSize = GetCheckImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
3556     return aSize;
3557 }
3558 
GetCheckImage(const AllSettings & rSettings,DrawButtonFlags nFlags)3559 Image CheckBox::GetCheckImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
3560 {
3561     ImplSVData*             pSVData = ImplGetSVData();
3562     const StyleSettings&    rStyleSettings = rSettings.GetStyleSettings();
3563     sal_uInt16              nStyle = 0;
3564 
3565     if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
3566         nStyle = STYLE_CHECKBOX_MONO;
3567 
3568     if ( pSVData->maCtrlData.maCheckImgList.empty() ||
3569          (pSVData->maCtrlData.mnCheckStyle != nStyle) ||
3570          (pSVData->maCtrlData.mnLastCheckFColor != rStyleSettings.GetFaceColor()) ||
3571          (pSVData->maCtrlData.mnLastCheckWColor != rStyleSettings.GetWindowColor()) ||
3572          (pSVData->maCtrlData.mnLastCheckLColor != rStyleSettings.GetLightColor()) )
3573     {
3574         pSVData->maCtrlData.maCheckImgList.clear();
3575 
3576         pSVData->maCtrlData.mnLastCheckFColor = rStyleSettings.GetFaceColor();
3577         pSVData->maCtrlData.mnLastCheckWColor = rStyleSettings.GetWindowColor();
3578         pSVData->maCtrlData.mnLastCheckLColor = rStyleSettings.GetLightColor();
3579 
3580         std::vector<OUString> aResources;
3581         if (nStyle)
3582         {
3583             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO1);
3584             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO2);
3585             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO3);
3586             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO4);
3587             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO5);
3588             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO6);
3589             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO7);
3590             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO8);
3591             aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO9);
3592         }
3593         else
3594         {
3595             aResources.emplace_back(SV_RESID_BITMAP_CHECK1);
3596             aResources.emplace_back(SV_RESID_BITMAP_CHECK2);
3597             aResources.emplace_back(SV_RESID_BITMAP_CHECK3);
3598             aResources.emplace_back(SV_RESID_BITMAP_CHECK4);
3599             aResources.emplace_back(SV_RESID_BITMAP_CHECK5);
3600             aResources.emplace_back(SV_RESID_BITMAP_CHECK6);
3601             aResources.emplace_back(SV_RESID_BITMAP_CHECK7);
3602             aResources.emplace_back(SV_RESID_BITMAP_CHECK8);
3603             aResources.emplace_back(SV_RESID_BITMAP_CHECK9);
3604         }
3605         LoadThemedImageList(rStyleSettings, pSVData->maCtrlData.maCheckImgList, aResources);
3606         pSVData->maCtrlData.mnCheckStyle = nStyle;
3607     }
3608 
3609     sal_uInt16 nIndex;
3610     if ( nFlags & DrawButtonFlags::Disabled )
3611     {
3612         if ( nFlags & DrawButtonFlags::DontKnow )
3613             nIndex = 8;
3614         else if ( nFlags & DrawButtonFlags::Checked )
3615             nIndex = 5;
3616         else
3617             nIndex = 4;
3618     }
3619     else if ( nFlags & DrawButtonFlags::Pressed )
3620     {
3621         if ( nFlags & DrawButtonFlags::DontKnow )
3622             nIndex = 7;
3623         else if ( nFlags & DrawButtonFlags::Checked )
3624             nIndex = 3;
3625         else
3626             nIndex = 2;
3627     }
3628     else
3629     {
3630         if ( nFlags & DrawButtonFlags::DontKnow )
3631             nIndex = 6;
3632         else if ( nFlags & DrawButtonFlags::Checked )
3633             nIndex = 1;
3634         else
3635             nIndex = 0;
3636     }
3637     return pSVData->maCtrlData.maCheckImgList[nIndex];
3638 }
3639 
ImplAdjustNWFSizes()3640 void CheckBox::ImplAdjustNWFSizes()
3641 {
3642     GetOutDev()->Push( PushFlags::MAPMODE );
3643     SetMapMode(MapMode(MapUnit::MapPixel));
3644 
3645     ImplControlValue aControlValue;
3646     Size aCurSize( GetSizePixel() );
3647     tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
3648     tools::Rectangle aBoundingRgn, aContentRgn;
3649 
3650     // get native size of a radiobutton
3651     if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3652                                 ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
3653                                 aBoundingRgn, aContentRgn ) )
3654     {
3655         Size aSize = aContentRgn.GetSize();
3656 
3657         if( aSize.Height() > aCurSize.Height() )
3658         {
3659             aCurSize.setHeight( aSize.Height() );
3660             SetSizePixel( aCurSize );
3661         }
3662     }
3663 
3664     GetOutDev()->Pop();
3665 }
3666 
CalcMinimumSize(tools::Long nMaxWidth) const3667 Size CheckBox::CalcMinimumSize( tools::Long nMaxWidth ) const
3668 {
3669     Size aSize = ImplGetCheckImageSize();
3670     nMaxWidth -= aSize.Width();
3671 
3672     OUString aText = GetText();
3673     if (!aText.isEmpty())
3674     {
3675         // subtract what will be added later
3676         nMaxWidth-=2;
3677         nMaxWidth -= ImplGetImageToTextDistance();
3678 
3679         Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
3680                                       aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
3681         aSize.AdjustWidth(2 );    // for focus rect
3682         aSize.AdjustWidth(ImplGetImageToTextDistance() );
3683         aSize.AdjustWidth(aTextSize.Width() );
3684         if ( aSize.Height() < aTextSize.Height() )
3685             aSize.setHeight( aTextSize.Height() );
3686     }
3687     else
3688     {
3689         // is this still correct ? since the checkbox now
3690         // shows a focus rect it should be 2 pixels wider and longer
3691 /* since otherwise the controls in the Writer hang too far up
3692         aSize.Width() += 2;
3693         aSize.Height() += 2;
3694 */
3695     }
3696 
3697     return CalcWindowSize( aSize );
3698 }
3699 
GetOptimalSize() const3700 Size CheckBox::GetOptimalSize() const
3701 {
3702     int nWidthRequest(get_width_request());
3703     return CalcMinimumSize(nWidthRequest != -1 ? nWidthRequest : 0);
3704 }
3705 
ShowFocus(const tools::Rectangle & rRect)3706 void CheckBox::ShowFocus(const tools::Rectangle& rRect)
3707 {
3708     if (IsNativeControlSupported(ControlType::Checkbox, ControlPart::Focus))
3709     {
3710         ImplControlValue aControlValue;
3711         tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
3712 
3713         aInRect.SetLeft( rRect.Left() );  // exclude the checkbox itself from the focusrect
3714 
3715         GetOutDev()->DrawNativeControl(ControlType::Checkbox, ControlPart::Focus, aInRect,
3716                           ControlState::FOCUSED, aControlValue, OUString());
3717     }
3718     Button::ShowFocus(rRect);
3719 }
3720 
DumpAsPropertyTree(tools::JsonWriter & rJsonWriter)3721 void CheckBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
3722 {
3723     Button::DumpAsPropertyTree(rJsonWriter);
3724     rJsonWriter.put("checked", IsChecked());
3725 }
3726 
GetUITestFactory() const3727 FactoryFunction CheckBox::GetUITestFactory() const
3728 {
3729     return CheckBoxUIObject::create;
3730 }
3731 
ImageButton(vcl::Window * pParent,WinBits nStyle)3732 ImageButton::ImageButton( vcl::Window* pParent, WinBits nStyle ) :
3733     PushButton( pParent, nStyle )
3734 {
3735     ImplInitStyle();
3736 }
3737 
ImplInitStyle()3738 void ImageButton::ImplInitStyle()
3739 {
3740     WinBits nStyle = GetStyle();
3741 
3742     if ( ! ( nStyle & ( WB_RIGHT | WB_LEFT ) ) )
3743         nStyle |= WB_CENTER;
3744 
3745     if ( ! ( nStyle & ( WB_TOP | WB_BOTTOM ) ) )
3746         nStyle |= WB_VCENTER;
3747 
3748     SetStyle( nStyle );
3749 }
3750 
3751 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3752