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