sdext: adapt xpdfwrapper to poppler 24.12
[LibreOffice.git] / vcl / source / control / button.cxx
blob4f8eafa7a14484b08feb594f89ab589482d9ded7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/poly.hxx>
22 #include <vcl/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>
39 #include <bitmaps.hlst>
40 #include <svdata.hxx>
41 #include <window.h>
42 #include <vclstatuslistener.hxx>
43 #include <osl/diagnose.h>
45 #include <comphelper/base64.hxx>
46 #include <comphelper/dispatchcommand.hxx>
47 #include <comphelper/lok.hxx>
48 #include <officecfg/Office/Common.hxx>
49 #include <boost/property_tree/ptree.hpp>
50 #include <tools/json_writer.hxx>
51 #include <tools/stream.hxx>
54 using namespace css;
56 constexpr auto PUSHBUTTON_VIEW_STYLE = WB_3DLOOK |
57 WB_LEFT | WB_CENTER | WB_RIGHT |
58 WB_TOP | WB_VCENTER | WB_BOTTOM |
59 WB_WORDBREAK | WB_NOLABEL |
60 WB_DEFBUTTON | WB_NOLIGHTBORDER |
61 WB_RECTSTYLE | WB_SMALLSTYLE |
62 WB_TOGGLE;
63 constexpr auto RADIOBUTTON_VIEW_STYLE = WB_3DLOOK |
64 WB_LEFT | WB_CENTER | WB_RIGHT |
65 WB_TOP | WB_VCENTER | WB_BOTTOM |
66 WB_WORDBREAK | WB_NOLABEL;
67 constexpr auto CHECKBOX_VIEW_STYLE = WB_3DLOOK |
68 WB_LEFT | WB_CENTER | WB_RIGHT |
69 WB_TOP | WB_VCENTER | WB_BOTTOM |
70 WB_WORDBREAK | WB_NOLABEL;
72 #define STYLE_RADIOBUTTON_MONO (sal_uInt16(0x0001)) // legacy
73 #define STYLE_CHECKBOX_MONO (sal_uInt16(0x0001)) // legacy
75 class ImplCommonButtonData
77 public:
78 ImplCommonButtonData();
80 tools::Rectangle maFocusRect;
81 tools::Long mnSeparatorX;
82 DrawButtonFlags mnButtonState;
83 bool mbSmallSymbol;
84 bool mbGeneratedTooltip;
86 Image maImage;
87 ImageAlign meImageAlign;
88 SymbolAlign meSymbolAlign;
90 Image maCustomContentImage;
92 /** StatusListener. Updates the button as the slot state changes */
93 rtl::Reference<VclStatusListener<Button>> mpStatusListener;
96 ImplCommonButtonData::ImplCommonButtonData() : mnSeparatorX(0), mnButtonState(DrawButtonFlags::NONE),
97 mbSmallSymbol(false), mbGeneratedTooltip(false), meImageAlign(ImageAlign::Top), meSymbolAlign(SymbolAlign::LEFT)
101 Button::Button( WindowType nType ) :
102 Control( nType ),
103 mpButtonData( std::make_unique<ImplCommonButtonData>() )
107 Button::~Button()
109 disposeOnce();
112 void Button::dispose()
114 if (mpButtonData->mpStatusListener.is())
115 mpButtonData->mpStatusListener->dispose();
116 Control::dispose();
119 void Button::SetCommandHandler(const OUString& aCommand, const css::uno::Reference<css::frame::XFrame>& rFrame)
121 maCommand = aCommand;
122 SetClickHdl( LINK( this, Button, dispatchCommandHandler) );
124 mpButtonData->mpStatusListener = new VclStatusListener<Button>(this, rFrame, aCommand);
125 mpButtonData->mpStatusListener->startListening();
128 void Button::Click()
130 ImplCallEventListenersAndHandler( VclEventId::ButtonClick, [this] () { maClickHdl.Call(this); } );
133 void Button::SetModeImage( const Image& rImage )
135 if ( rImage != mpButtonData->maImage )
137 mpButtonData->maImage = rImage;
138 StateChanged( StateChangedType::Data );
139 queue_resize();
143 Image const & Button::GetModeImage( ) const
145 return mpButtonData->maImage;
148 bool Button::HasImage() const
150 return !!(mpButtonData->maImage);
153 void Button::SetImageAlign( ImageAlign eAlign )
155 if ( mpButtonData->meImageAlign != eAlign )
157 mpButtonData->meImageAlign = eAlign;
158 StateChanged( StateChangedType::Data );
162 ImageAlign Button::GetImageAlign() const
164 return mpButtonData->meImageAlign;
167 void Button::SetCustomButtonImage(const Image& rImage)
169 if (rImage != mpButtonData->maCustomContentImage)
171 mpButtonData->maCustomContentImage = rImage;
172 StateChanged( StateChangedType::Data );
176 Image const & Button::GetCustomButtonImage() const
178 return mpButtonData->maCustomContentImage;
181 tools::Long Button::ImplGetSeparatorX() const
183 return mpButtonData->mnSeparatorX;
186 void Button::ImplSetSeparatorX( tools::Long nX )
188 mpButtonData->mnSeparatorX = nX;
191 DrawTextFlags Button::ImplGetTextStyle( WinBits nWinStyle, SystemTextColorFlags nSystemTextColorFlags ) const
193 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
194 DrawTextFlags nTextStyle = FixedText::ImplGetTextStyle(nWinStyle & ~WB_DEFBUTTON);
196 if (!IsEnabled())
197 nTextStyle |= DrawTextFlags::Disable;
199 if ((nSystemTextColorFlags & SystemTextColorFlags::Mono) ||
200 (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
202 nTextStyle |= DrawTextFlags::Mono;
205 return nTextStyle;
208 void Button::ImplDrawAlignedImage(OutputDevice* pDev, Point& rPos,
209 Size& rSize,
210 sal_Int32 nImageSep,
211 DrawTextFlags nTextStyle, tools::Rectangle *pSymbolRect,
212 bool bAddImageSep)
214 OUString aText(GetText());
215 bool bDrawImage = HasImage();
216 bool bDrawText = !aText.isEmpty();
217 bool bHasSymbol = pSymbolRect != nullptr;
219 // No text and no image => nothing to do => return
220 if (!bDrawImage && !bDrawText && !bHasSymbol)
221 return;
223 WinBits nWinStyle = GetStyle();
224 tools::Rectangle aOutRect( rPos, rSize );
225 ImageAlign eImageAlign = mpButtonData->meImageAlign;
226 Size aImageSize = mpButtonData->maImage.GetSizePixel();
228 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
229 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
231 // Drawing text or symbol only is simple, use style and output rectangle
232 if (bHasSymbol && !bDrawImage && !bDrawText)
234 *pSymbolRect = aOutRect;
235 return;
237 else if (bDrawText && !bDrawImage && !bHasSymbol)
239 aOutRect = DrawControlText(*pDev, aOutRect, aText, nTextStyle, nullptr, nullptr);
240 tools::Rectangle textRect = GetTextRect(
241 tools::Rectangle(Point(), Size(0x7fffffff, 0x7fffffff)), aText, nTextStyle);
242 // If the button text doesn't fit into it, put it into a tooltip (might happen in sidebar)
243 if (GetQuickHelpText()!= aText && mpButtonData->mbGeneratedTooltip)
244 SetQuickHelpText(u""_ustr);
245 if (GetQuickHelpText().isEmpty() && textRect.getOpenWidth() > rSize.getWidth())
247 SetQuickHelpText(aText);
248 mpButtonData->mbGeneratedTooltip = true;
251 ImplSetFocusRect(aOutRect);
252 rSize = aOutRect.GetSize();
253 rPos = aOutRect.TopLeft();
255 return;
258 // check for HC mode ( image only! )
259 Image* pImage = &(mpButtonData->maImage);
261 Size aTextSize;
262 Size aSymbolSize;
263 Size aDeviceTextSize;
264 Point aImagePos = rPos;
265 Point aTextPos = rPos;
266 tools::Rectangle aUnion(aImagePos, aImageSize);
267 tools::Long nSymbolHeight = 0;
269 if (bDrawText || bHasSymbol)
271 // Get the size of the text output area ( the symbol will be drawn in
272 // this area as well, so the symbol rectangle will be calculated here, too )
274 tools::Rectangle aRect(Point(), rSize);
275 Size aTSSize;
277 if (bHasSymbol)
279 tools::Rectangle aSymbol;
280 if (bDrawText)
282 nSymbolHeight = pDev->GetTextHeight();
283 if (mpButtonData->mbSmallSymbol)
284 nSymbolHeight = nSymbolHeight * 3 / 4;
286 aSymbol = tools::Rectangle(Point(), Size(nSymbolHeight, nSymbolHeight));
287 ImplCalcSymbolRect(aSymbol);
288 aRect.AdjustLeft(3 * nSymbolHeight / 2 );
289 aTSSize.setWidth( 3 * nSymbolHeight / 2 );
291 else
293 aSymbol = tools::Rectangle(Point(), rSize);
294 ImplCalcSymbolRect(aSymbol);
295 aTSSize.setWidth( aSymbol.GetWidth() );
297 aTSSize.setHeight( aSymbol.GetHeight() );
298 aSymbolSize = aSymbol.GetSize();
301 if (bDrawText)
303 if ((eImageAlign == ImageAlign::LeftTop) ||
304 (eImageAlign == ImageAlign::Left ) ||
305 (eImageAlign == ImageAlign::LeftBottom) ||
306 (eImageAlign == ImageAlign::RightTop) ||
307 (eImageAlign == ImageAlign::Right) ||
308 (eImageAlign == ImageAlign::RightBottom))
310 aRect.AdjustRight( -sal_Int32(aImageSize.Width() + nImageSep) );
312 else if ((eImageAlign == ImageAlign::TopLeft) ||
313 (eImageAlign == ImageAlign::Top) ||
314 (eImageAlign == ImageAlign::TopRight) ||
315 (eImageAlign == ImageAlign::BottomLeft) ||
316 (eImageAlign == ImageAlign::Bottom) ||
317 (eImageAlign == ImageAlign::BottomRight))
319 aRect.AdjustBottom( -sal_Int32(aImageSize.Height() + nImageSep) );
322 aRect = GetControlTextRect(*pDev, aRect, aText, nTextStyle, &aDeviceTextSize);
323 aTextSize = aRect.GetSize();
325 aTSSize.AdjustWidth(aTextSize.Width() );
327 if (aTSSize.Height() < aTextSize.Height())
328 aTSSize.setHeight( aTextSize.Height() );
330 if (bAddImageSep && bDrawImage)
332 tools::Long nDiff = (aImageSize.Height() - aTextSize.Height()) / 3;
333 if (nDiff > 0)
334 nImageSep += nDiff;
338 Size aMax;
339 aMax.setWidth( std::max(aTSSize.Width(), aImageSize.Width()) );
340 aMax.setHeight( std::max(aTSSize.Height(), aImageSize.Height()) );
342 // Now calculate the output area for the image and the text according to the image align flags
344 if ((eImageAlign == ImageAlign::Left) ||
345 (eImageAlign == ImageAlign::Right))
347 aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
348 aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
350 else if ((eImageAlign == ImageAlign::LeftBottom) ||
351 (eImageAlign == ImageAlign::RightBottom))
353 aImagePos.setY( rPos.Y() + aMax.Height() - aImageSize.Height() );
354 aTextPos.setY( rPos.Y() + aMax.Height() - aTSSize.Height() );
356 else if ((eImageAlign == ImageAlign::Top) ||
357 (eImageAlign == ImageAlign::Bottom))
359 aImagePos.setX( rPos.X() + (aMax.Width() - aImageSize.Width()) / 2 );
360 aTextPos.setX( rPos.X() + (aMax.Width() - aTSSize.Width()) / 2 );
362 else if ((eImageAlign == ImageAlign::TopRight) ||
363 (eImageAlign == ImageAlign::BottomRight))
365 aImagePos.setX( rPos.X() + aMax.Width() - aImageSize.Width() );
366 aTextPos.setX( rPos.X() + aMax.Width() - aTSSize.Width() );
369 if ((eImageAlign == ImageAlign::LeftTop) ||
370 (eImageAlign == ImageAlign::Left) ||
371 (eImageAlign == ImageAlign::LeftBottom))
373 aTextPos.setX( rPos.X() + aImageSize.Width() + nImageSep );
375 else if ((eImageAlign == ImageAlign::RightTop) ||
376 (eImageAlign == ImageAlign::Right) ||
377 (eImageAlign == ImageAlign::RightBottom))
379 aImagePos.setX( rPos.X() + aTSSize.Width() + nImageSep );
381 else if ((eImageAlign == ImageAlign::TopLeft) ||
382 (eImageAlign == ImageAlign::Top) ||
383 (eImageAlign == ImageAlign::TopRight))
385 aTextPos.setY( rPos.Y() + aImageSize.Height() + nImageSep );
387 else if ((eImageAlign == ImageAlign::BottomLeft) ||
388 (eImageAlign == ImageAlign::Bottom) ||
389 (eImageAlign == ImageAlign::BottomRight))
391 aImagePos.setY( rPos.Y() + aTSSize.Height() + nImageSep );
393 else if (eImageAlign == ImageAlign::Center)
395 aImagePos.setX( rPos.X() + (aMax.Width() - aImageSize.Width()) / 2 );
396 aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
397 aTextPos.setX( rPos.X() + (aMax.Width() - aTSSize.Width()) / 2 );
398 aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
400 aUnion = tools::Rectangle(aImagePos, aImageSize);
401 aUnion.Union(tools::Rectangle(aTextPos, aTSSize));
404 // Now place the combination of text and image in the output area of the button
405 // according to the window style (WinBits)
406 tools::Long nXOffset = 0;
407 tools::Long nYOffset = 0;
409 if (nWinStyle & WB_CENTER)
411 nXOffset = (rSize.Width() - aUnion.GetWidth()) / 2;
413 else if (nWinStyle & WB_RIGHT)
415 nXOffset = rSize.Width() - aUnion.GetWidth();
418 if (nWinStyle & WB_VCENTER)
420 nYOffset = (rSize.Height() - aUnion.GetHeight()) / 2;
422 else if (nWinStyle & WB_BOTTOM)
424 nYOffset = rSize.Height() - aUnion.GetHeight();
427 // the top left corner should always be visible, so we don't allow negative offsets
428 if (nXOffset < 0) nXOffset = 0;
429 if (nYOffset < 0) nYOffset = 0;
431 aImagePos.AdjustX(nXOffset );
432 aImagePos.AdjustY(nYOffset );
433 aTextPos.AdjustX(nXOffset );
434 aTextPos.AdjustY(nYOffset );
436 // set rPos and rSize to the union
437 rSize = aUnion.GetSize();
438 rPos.AdjustX(nXOffset );
439 rPos.AdjustY(nYOffset );
441 if (bHasSymbol)
443 if (mpButtonData->meSymbolAlign == SymbolAlign::RIGHT)
445 Point aRightPos(aTextPos.X() + aTextSize.Width() + aSymbolSize.Width() / 2, aTextPos.Y());
446 *pSymbolRect = tools::Rectangle(aRightPos, aSymbolSize);
448 else
450 *pSymbolRect = tools::Rectangle(aTextPos, aSymbolSize);
451 aTextPos.AdjustX(3 * nSymbolHeight / 2 );
453 if (mpButtonData->mbSmallSymbol)
455 nYOffset = (aUnion.GetHeight() - aSymbolSize.Height()) / 2;
456 pSymbolRect->SetPosY(aTextPos.Y() + nYOffset);
460 DrawImageFlags nStyle = DrawImageFlags::NONE;
462 if (!IsEnabled())
464 nStyle |= DrawImageFlags::Disable;
467 if (IsZoom())
468 pDev->DrawImage(aImagePos, aImageSize, *pImage, nStyle);
469 else
470 pDev->DrawImage(aImagePos, *pImage, nStyle);
472 if (bDrawText)
474 const tools::Rectangle aTOutRect(aTextPos, aTextSize);
475 ImplSetFocusRect(aTOutRect);
476 DrawControlText(*pDev, aTOutRect, aText, nTextStyle, nullptr, nullptr, &aDeviceTextSize);
478 else
480 ImplSetFocusRect(tools::Rectangle(aImagePos, aImageSize));
484 void Button::ImplSetFocusRect(const tools::Rectangle &rFocusRect)
486 tools::Rectangle aFocusRect = rFocusRect;
487 tools::Rectangle aOutputRect(Point(), GetOutputSizePixel());
489 if (!aFocusRect.IsEmpty())
491 aFocusRect.AdjustLeft( -1 );
492 aFocusRect.AdjustTop( -1 );
493 aFocusRect.AdjustRight( 1 );
494 aFocusRect.AdjustBottom( 1 );
497 if (aFocusRect.Left() < aOutputRect.Left())
498 aFocusRect.SetLeft( aOutputRect.Left() );
499 if (aFocusRect.Top() < aOutputRect.Top())
500 aFocusRect.SetTop( aOutputRect.Top() );
501 if (aFocusRect.Right() > aOutputRect.Right())
502 aFocusRect.SetRight( aOutputRect.Right() );
503 if (aFocusRect.Bottom() > aOutputRect.Bottom())
504 aFocusRect.SetBottom( aOutputRect.Bottom() );
506 mpButtonData->maFocusRect = aFocusRect;
509 const tools::Rectangle& Button::ImplGetFocusRect() const
511 return mpButtonData->maFocusRect;
514 DrawButtonFlags& Button::GetButtonState()
516 return mpButtonData->mnButtonState;
519 DrawButtonFlags Button::GetButtonState() const
521 return mpButtonData->mnButtonState;
524 void Button::ImplSetSymbolAlign( SymbolAlign eAlign )
526 if ( mpButtonData->meSymbolAlign != eAlign )
528 mpButtonData->meSymbolAlign = eAlign;
529 StateChanged( StateChangedType::Data );
533 void Button::SetSmallSymbol()
535 mpButtonData->mbSmallSymbol = true;
538 bool Button::IsSmallSymbol () const
540 return mpButtonData->mbSmallSymbol;
543 bool Button::set_property(const OUString &rKey, const OUString &rValue)
545 if (rKey == "image-position")
547 ImageAlign eAlign = ImageAlign::Left;
548 if (rValue == "left")
549 eAlign = ImageAlign::Left;
550 else if (rValue == "right")
551 eAlign = ImageAlign::Right;
552 else if (rValue == "top")
553 eAlign = ImageAlign::Top;
554 else if (rValue == "bottom")
555 eAlign = ImageAlign::Bottom;
556 SetImageAlign(eAlign);
558 else if (rKey == "focus-on-click")
560 WinBits nBits = GetStyle();
561 nBits &= ~WB_NOPOINTERFOCUS;
562 if (!toBool(rValue))
563 nBits |= WB_NOPOINTERFOCUS;
564 SetStyle(nBits);
566 else
567 return Control::set_property(rKey, rValue);
568 return true;
571 void Button::statusChanged(const css::frame::FeatureStateEvent& rEvent)
573 Enable(rEvent.IsEnabled);
576 FactoryFunction Button::GetUITestFactory() const
578 return ButtonUIObject::create;
581 namespace
584 std::string_view symbolTypeName(SymbolType eSymbolType)
586 switch (eSymbolType)
588 case SymbolType::DONTKNOW: return "DONTKNOW";
589 case SymbolType::IMAGE: return "IMAGE";
590 case SymbolType::ARROW_UP: return "ARROW_UP";
591 case SymbolType::ARROW_DOWN: return "ARROW_DOWN";
592 case SymbolType::ARROW_LEFT: return "ARROW_LEFT";
593 case SymbolType::ARROW_RIGHT: return "ARROW_RIGHT";
594 case SymbolType::SPIN_UP: return "SPIN_UP";
595 case SymbolType::SPIN_DOWN: return "SPIN_DOWN";
596 case SymbolType::SPIN_LEFT: return "SPIN_LEFT";
597 case SymbolType::SPIN_RIGHT: return "SPIN_RIGHT";
598 case SymbolType::FIRST: return "FIRST";
599 case SymbolType::LAST: return "LAST";
600 case SymbolType::PREV: return "PREV";
601 case SymbolType::NEXT: return "NEXT";
602 case SymbolType::PAGEUP: return "PAGEUP";
603 case SymbolType::PAGEDOWN: return "PAGEDOWN";
604 case SymbolType::PLAY: return "PLAY";
605 case SymbolType::STOP: return "STOP";
606 case SymbolType::CLOSE: return "CLOSE";
607 case SymbolType::CHECKMARK: return "CHECKMARK";
608 case SymbolType::RADIOCHECKMARK: return "RADIOCHECKMARK";
609 case SymbolType::FLOAT: return "FLOAT";
610 case SymbolType::DOCK: return "DOCK";
611 case SymbolType::HIDE: return "HIDE";
612 case SymbolType::HELP: return "HELP";
613 case SymbolType::PLUS: return "PLUS";
616 return "UNKNOWN";
621 void Button::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
623 Control::DumpAsPropertyTree(rJsonWriter);
624 rJsonWriter.put("text", GetText());
625 if (HasImage())
627 SvMemoryStream aOStm(6535, 6535);
628 if(GraphicConverter::Export(aOStm, GetModeImage().GetBitmapEx(), ConvertDataFormat::PNG) == ERRCODE_NONE)
630 css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
631 OStringBuffer aBuffer("data:image/png;base64,");
632 ::comphelper::Base64::encode(aBuffer, aSeq);
633 rJsonWriter.put("image", aBuffer);
637 if (GetStyle() & WB_DEFBUTTON)
638 rJsonWriter.put("has_default", true);
641 void PushButton::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
643 Button::DumpAsPropertyTree(rJsonWriter);
644 if (GetSymbol() != SymbolType::DONTKNOW)
645 rJsonWriter.put("symbol", symbolTypeName(GetSymbol()));
646 if (isToggleButton())
647 rJsonWriter.put("isToggle", true);
650 IMPL_STATIC_LINK( Button, dispatchCommandHandler, Button*, pButton, void )
652 if (pButton == nullptr)
653 return;
655 comphelper::dispatchCommand(pButton->maCommand, uno::Sequence<beans::PropertyValue>());
658 void PushButton::ImplInitPushButtonData()
660 mpWindowImpl->mbPushButton = true;
662 meSymbol = SymbolType::DONTKNOW;
663 meState = TRISTATE_FALSE;
664 mnDDStyle = PushButtonDropdownStyle::NONE;
665 mbIsActive = false;
666 mbPressed = false;
667 mbIsAction = false;
670 namespace
672 vcl::Window* getPreviousSibling(vcl::Window const *pParent)
674 return pParent ? pParent->GetWindow(GetWindowType::LastChild) : nullptr;
678 void PushButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
680 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
681 Button::ImplInit( pParent, nStyle, nullptr );
683 if ( nStyle & WB_NOLIGHTBORDER )
684 GetButtonState() |= DrawButtonFlags::NoLightBorder;
686 ImplInitSettings( true );
689 WinBits PushButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
691 if ( !(nStyle & WB_NOTABSTOP) )
692 nStyle |= WB_TABSTOP;
694 // if no alignment is given, default to "vertically centered". This is because since
695 // #i26046#, we respect the vertical alignment flags (previously we didn't completely),
696 // but we of course want to look as before when no vertical alignment is specified
697 if ( ( nStyle & ( WB_TOP | WB_VCENTER | WB_BOTTOM ) ) == 0 )
698 nStyle |= WB_VCENTER;
700 if ( !(nStyle & WB_NOGROUP) &&
701 (!pPrevWindow ||
702 ((pPrevWindow->GetType() != WindowType::PUSHBUTTON ) &&
703 (pPrevWindow->GetType() != WindowType::OKBUTTON ) &&
704 (pPrevWindow->GetType() != WindowType::CANCELBUTTON) &&
705 (pPrevWindow->GetType() != WindowType::HELPBUTTON )) ) )
706 nStyle |= WB_GROUP;
707 return nStyle;
710 const vcl::Font& PushButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
712 return _rStyle.GetPushButtonFont();
715 const Color& PushButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
717 return _rStyle.GetButtonTextColor();
720 void PushButton::ImplInitSettings( bool bBackground )
722 Button::ImplInitSettings();
724 if ( !bBackground )
725 return;
727 SetBackground();
728 // #i38498#: do not check for GetParent()->IsChildTransparentModeEnabled()
729 // otherwise the formcontrol button will be overdrawn due to ParentClipMode::NoClip
730 // for radio and checkbox this is ok as they should appear transparent in documents
731 if ( IsNativeControlSupported( ControlType::Pushbutton, ControlPart::Entire ) ||
732 (GetStyle() & WB_FLATBUTTON) != 0 )
734 EnableChildTransparentMode();
735 SetParentClipMode( ParentClipMode::NoClip );
736 SetPaintTransparent( true );
738 if ((GetStyle() & WB_FLATBUTTON) == 0)
739 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
740 else
741 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRectsForFlatButtons;
743 else
745 EnableChildTransparentMode( false );
746 SetParentClipMode();
747 SetPaintTransparent( false );
751 void PushButton::ImplDrawPushButtonFrame(vcl::RenderContext& rRenderContext,
752 tools::Rectangle& rRect, DrawButtonFlags nStyle)
754 if (!(GetStyle() & (WB_RECTSTYLE | WB_SMALLSTYLE)))
756 StyleSettings aStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
757 if (IsControlBackground())
758 aStyleSettings.Set3DColors(GetControlBackground());
761 DecorationView aDecoView(&rRenderContext);
762 if (IsControlBackground())
764 AllSettings aSettings = rRenderContext.GetSettings();
765 AllSettings aOldSettings = aSettings;
766 StyleSettings aStyleSettings = aSettings.GetStyleSettings();
767 if (nStyle & DrawButtonFlags::Highlight)
769 // with the custom background, native highlight do nothing, so code below mimic
770 // native highlight by changing luminance
771 Color controlBackgroundColorHighlighted = GetControlBackground();
772 sal_uInt8 colorLuminance = controlBackgroundColorHighlighted.GetLuminance();
773 if (colorLuminance < 205)
774 controlBackgroundColorHighlighted.IncreaseLuminance(50);
775 else
776 controlBackgroundColorHighlighted.DecreaseLuminance(50);
777 aStyleSettings.Set3DColors(controlBackgroundColorHighlighted);
779 else
780 aStyleSettings.Set3DColors(GetControlBackground());
781 aSettings.SetStyleSettings(aStyleSettings);
783 // Call OutputDevice::SetSettings() explicitly, as rRenderContext may
784 // be a vcl::Window in fact, and vcl::Window::SetSettings() will call
785 // Invalidate(), which is a problem, since we're in Paint().
786 rRenderContext.OutputDevice::SetSettings(aSettings);
787 rRect = aDecoView.DrawButton(rRect, nStyle);
788 rRenderContext.OutputDevice::SetSettings(aOldSettings);
790 else
791 rRect = aDecoView.DrawButton(rRect, nStyle);
794 bool PushButton::ImplHitTestPushButton( vcl::Window const * pDev,
795 const Point& rPos )
797 tools::Rectangle aTestRect( Point(), pDev->GetOutputSizePixel() );
799 return aTestRect.Contains( rPos );
802 DrawTextFlags PushButton::ImplGetTextStyle( SystemTextColorFlags nSystemTextColorFlags ) const
804 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
806 DrawTextFlags nTextStyle = DrawTextFlags::Mnemonic | DrawTextFlags::MultiLine | DrawTextFlags::EndEllipsis;
808 if ( ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono ) ||
809 ( nSystemTextColorFlags & SystemTextColorFlags::Mono ) )
810 nTextStyle |= DrawTextFlags::Mono;
812 if ( GetStyle() & WB_WORDBREAK )
813 nTextStyle |= DrawTextFlags::WordBreak;
814 if ( GetStyle() & WB_NOLABEL )
815 nTextStyle &= ~DrawTextFlags::Mnemonic;
817 if ( GetStyle() & WB_LEFT )
818 nTextStyle |= DrawTextFlags::Left;
819 else if ( GetStyle() & WB_RIGHT )
820 nTextStyle |= DrawTextFlags::Right;
821 else
822 nTextStyle |= DrawTextFlags::Center;
824 if ( GetStyle() & WB_TOP )
825 nTextStyle |= DrawTextFlags::Top;
826 else if ( GetStyle() & WB_BOTTOM )
827 nTextStyle |= DrawTextFlags::Bottom;
828 else
829 nTextStyle |= DrawTextFlags::VCenter;
831 if ( !IsEnabled() )
832 nTextStyle |= DrawTextFlags::Disable;
834 return nTextStyle;
837 void PushButton::ImplDrawPushButtonContent(OutputDevice *pDev, SystemTextColorFlags nSystemTextColorFlags,
838 const tools::Rectangle &rRect, bool bMenuBtnSep,
839 DrawButtonFlags nButtonFlags)
841 const StyleSettings &rStyleSettings = GetSettings().GetStyleSettings();
842 tools::Rectangle aInRect = rRect;
843 Color aColor;
844 DrawTextFlags nTextStyle = ImplGetTextStyle(nSystemTextColorFlags);
845 DrawSymbolFlags nStyle;
847 if (aInRect.Right() < aInRect.Left() || aInRect.Bottom() < aInRect.Top())
848 return;
850 pDev->Push(vcl::PushFlags::CLIPREGION);
851 pDev->IntersectClipRegion(aInRect);
853 if (nSystemTextColorFlags & SystemTextColorFlags::Mono)
854 aColor = COL_BLACK;
856 else if (IsControlForeground())
857 aColor = GetControlForeground();
859 // Button types with possibly different text coloring are flat buttons and regular buttons. Regular buttons may be action
860 // buttons and may have an additional default status. Moreover all buttons may have an additional pressed and rollover
861 // (highlight) status. Pressed buttons are always in rollover status.
863 else if (GetStyle() & WB_FLATBUTTON)
864 if (nButtonFlags & DrawButtonFlags::Pressed)
865 aColor = rStyleSettings.GetFlatButtonPressedRolloverTextColor();
866 else if (nButtonFlags & DrawButtonFlags::Highlight)
867 aColor = rStyleSettings.GetFlatButtonRolloverTextColor();
868 else
869 aColor = rStyleSettings.GetFlatButtonTextColor();
870 else
871 if (isAction() && (nButtonFlags & DrawButtonFlags::Default))
872 if (nButtonFlags & DrawButtonFlags::Pressed)
873 aColor = rStyleSettings.GetDefaultActionButtonPressedRolloverTextColor();
874 else if (nButtonFlags & DrawButtonFlags::Highlight)
875 aColor = rStyleSettings.GetDefaultActionButtonRolloverTextColor();
876 else
877 aColor = rStyleSettings.GetDefaultActionButtonTextColor();
878 else if (isAction())
879 if (nButtonFlags & DrawButtonFlags::Pressed)
880 aColor = rStyleSettings.GetActionButtonPressedRolloverTextColor();
881 else if (nButtonFlags & DrawButtonFlags::Highlight)
882 aColor = rStyleSettings.GetActionButtonRolloverTextColor();
883 else
884 aColor = rStyleSettings.GetActionButtonTextColor();
885 else if (nButtonFlags & DrawButtonFlags::Default)
886 if (nButtonFlags & DrawButtonFlags::Pressed)
887 aColor = rStyleSettings.GetDefaultButtonPressedRolloverTextColor();
888 else if (nButtonFlags & DrawButtonFlags::Highlight)
889 aColor = rStyleSettings.GetDefaultButtonRolloverTextColor();
890 else
891 aColor = rStyleSettings.GetDefaultButtonTextColor();
892 else
893 if (nButtonFlags & DrawButtonFlags::Pressed)
894 aColor = rStyleSettings.GetButtonPressedRolloverTextColor();
895 else if (nButtonFlags & DrawButtonFlags::Highlight)
896 aColor = rStyleSettings.GetButtonRolloverTextColor();
897 else
898 aColor = rStyleSettings.GetButtonTextColor();
900 #if defined(MACOSX) || defined(IOS)
901 // tdf#152486 These are the buttons in infobars where the infobar has a custom
902 // background color and on these platforms the buttons blend with
903 // their background.
904 vcl::Window* pParent = GetParent();
905 if (pParent->get_id() == "ExtraButton")
907 while (pParent && !pParent->IsControlBackground())
908 pParent = pParent->GetParent();
909 if (pParent)
911 if (aColor.IsBright() && !pParent->GetControlBackground().IsDark())
912 aColor = COL_BLACK;
915 #endif
917 pDev->SetTextColor(aColor);
919 if ( IsEnabled() )
920 nStyle = DrawSymbolFlags::NONE;
921 else
922 nStyle = DrawSymbolFlags::Disable;
924 Size aSize = rRect.GetSize();
925 Point aPos = rRect.TopLeft();
927 sal_Int32 nImageSep = 1 + (pDev->GetTextHeight()-10)/2;
928 if( nImageSep < 1 )
929 nImageSep = 1;
930 if ( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
931 mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
933 tools::Long nSeparatorX = 0;
934 tools::Rectangle aSymbolRect = aInRect;
936 // calculate symbol size
937 tools::Long nSymbolSize = pDev->GetTextHeight() / 2 + 1;
938 if (nSymbolSize > aSize.Width() / 2)
939 nSymbolSize = aSize.Width() / 2;
941 nSeparatorX = aInRect.Right() - 2*nSymbolSize;
943 // tdf#141761 Minimum width should be (1) Pixel, see comment
944 // with same task number above for more info
945 const tools::Long nWidthAdjust(2*nSymbolSize);
946 aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
948 // center symbol rectangle in the separated area
949 aSymbolRect.AdjustRight( -(nSymbolSize/2) );
950 aSymbolRect.SetLeft( aSymbolRect.Right() - nSymbolSize );
952 ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
953 nTextStyle, nullptr, true );
955 tools::Long nDistance = (aSymbolRect.GetHeight() > 10) ? 2 : 1;
956 DecorationView aDecoView( pDev );
957 if( bMenuBtnSep && nSeparatorX > 0 )
959 Point aStartPt( nSeparatorX, aSymbolRect.Top()+nDistance );
960 Point aEndPt( nSeparatorX, aSymbolRect.Bottom()-nDistance );
961 aDecoView.DrawSeparator( aStartPt, aEndPt );
963 ImplSetSeparatorX( nSeparatorX );
965 aDecoView.DrawSymbol( aSymbolRect, SymbolType::SPIN_DOWN, aColor, nStyle );
968 else
970 tools::Rectangle aSymbolRect;
971 ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
972 nTextStyle, IsSymbol() ? &aSymbolRect : nullptr, true );
974 if ( IsSymbol() )
976 DecorationView aDecoView( pDev );
977 aDecoView.DrawSymbol( aSymbolRect, meSymbol, aColor, nStyle );
981 pDev->Pop(); // restore clipregion
984 void PushButton::ImplDrawPushButton(vcl::RenderContext& rRenderContext)
986 HideFocus();
988 DrawButtonFlags nButtonStyle = GetButtonState();
989 Size aOutSz(GetOutputSizePixel());
990 tools::Rectangle aRect(Point(), aOutSz);
991 tools::Rectangle aInRect = aRect;
992 bool bNativeOK = false;
994 // adjust style if button should be rendered 'pressed'
995 if (mbPressed || mbIsActive)
996 nButtonStyle |= DrawButtonFlags::Pressed;
998 if (GetStyle() & WB_FLATBUTTON)
999 nButtonStyle |= DrawButtonFlags::Flat;
1001 // TODO: move this to Window class or make it a member !!!
1002 ControlType aCtrlType = ControlType::Generic;
1003 switch(GetParent()->GetType())
1005 case WindowType::LISTBOX:
1006 case WindowType::MULTILISTBOX:
1007 case WindowType::TREELISTBOX:
1008 aCtrlType = ControlType::Listbox;
1009 break;
1011 case WindowType::COMBOBOX:
1012 case WindowType::PATTERNBOX:
1013 case WindowType::NUMERICBOX:
1014 case WindowType::METRICBOX:
1015 case WindowType::CURRENCYBOX:
1016 case WindowType::DATEBOX:
1017 case WindowType::TIMEBOX:
1018 case WindowType::LONGCURRENCYBOX:
1019 aCtrlType = ControlType::Combobox;
1020 break;
1021 default:
1022 break;
1025 bool bDropDown = (IsSymbol() && (GetSymbol() == SymbolType::SPIN_DOWN) && GetText().isEmpty());
1027 if( bDropDown && (aCtrlType == ControlType::Combobox || aCtrlType == ControlType::Listbox))
1029 if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::Entire))
1031 // skip painting if the button was already drawn by the theme
1032 if (aCtrlType == ControlType::Combobox)
1034 Edit* pEdit = static_cast<Edit*>(GetParent());
1035 if (pEdit->ImplUseNativeBorder(rRenderContext, pEdit->GetStyle()))
1036 bNativeOK = true;
1038 else if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::HasBackgroundTexture))
1040 bNativeOK = true;
1043 if (!bNativeOK && GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::ButtonDown))
1045 // let the theme draw it, note we then need support
1046 // for ControlType::Listbox/ControlPart::ButtonDown and ControlType::Combobox/ControlPart::ButtonDown
1048 ImplControlValue aControlValue;
1049 ControlState nState = ControlState::NONE;
1051 if (mbPressed || mbIsActive)
1052 nState |= ControlState::PRESSED;
1053 if (GetButtonState() & DrawButtonFlags::Pressed)
1054 nState |= ControlState::PRESSED;
1055 if (HasFocus())
1056 nState |= ControlState::FOCUSED;
1057 if (GetButtonState() & DrawButtonFlags::Default)
1058 nState |= ControlState::DEFAULT;
1059 if (Window::IsEnabled())
1060 nState |= ControlState::ENABLED;
1062 if (IsMouseOver() && aInRect.Contains(GetPointerPosPixel()))
1063 nState |= ControlState::ROLLOVER;
1065 if ( IsMouseOver() && aInRect.Contains(GetPointerPosPixel()) && mbIsActive)
1067 nState |= ControlState::ROLLOVER;
1068 nButtonStyle &= ~DrawButtonFlags::Pressed;
1071 bNativeOK = rRenderContext.DrawNativeControl(aCtrlType, ControlPart::ButtonDown, aInRect, nState,
1072 aControlValue, OUString());
1077 if (bNativeOK)
1078 return;
1080 bool bRollOver = (IsMouseOver() && aInRect.Contains(GetPointerPosPixel()));
1081 if (bRollOver)
1082 nButtonStyle |= DrawButtonFlags::Highlight;
1083 bool bDrawMenuSep = mnDDStyle == PushButtonDropdownStyle::SplitMenuButton;
1084 if (GetStyle() & WB_FLATBUTTON)
1086 if (!bRollOver && !HasFocus())
1087 bDrawMenuSep = false;
1089 // tdf#123175 if there is a custom control bg set, draw the button without outsourcing to the NWF
1090 bNativeOK = !IsControlBackground() && rRenderContext.IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire);
1091 if (bNativeOK)
1093 PushButtonValue aControlValue;
1094 aControlValue.mbIsAction = isAction();
1096 tools::Rectangle aCtrlRegion(aInRect);
1097 ControlState nState = ControlState::NONE;
1099 if (mbPressed || IsChecked() || mbIsActive)
1101 nState |= ControlState::PRESSED;
1102 nButtonStyle |= DrawButtonFlags::Pressed;
1104 if (GetButtonState() & DrawButtonFlags::Pressed)
1105 nState |= ControlState::PRESSED;
1106 if (HasFocus())
1107 nState |= ControlState::FOCUSED;
1108 if (GetButtonState() & DrawButtonFlags::Default)
1109 nState |= ControlState::DEFAULT;
1110 if (Window::IsEnabled())
1111 nState |= ControlState::ENABLED;
1113 if (bRollOver || mbIsActive)
1115 nButtonStyle |= DrawButtonFlags::Highlight;
1116 nState |= ControlState::ROLLOVER;
1119 if (mbIsActive && bRollOver)
1121 nState &= ~ControlState::PRESSED;
1122 nButtonStyle &= ~DrawButtonFlags::Pressed;
1125 if (GetStyle() & WB_FLATBUTTON)
1126 aControlValue.m_bFlatButton = true;
1128 // draw frame into invisible window to have aInRect modified correctly
1129 // but do not shift the inner rect for pressed buttons (ie remove DrawButtonFlags::Pressed)
1130 // this assumes the theme has enough visual cues to signalize the button was pressed
1131 //Window aWin( this );
1132 //ImplDrawPushButtonFrame( &aWin, aInRect, nButtonStyle & ~DrawButtonFlags::Pressed );
1134 // looks better this way as symbols were displaced slightly using the above approach
1135 aInRect.AdjustTop(4 );
1136 aInRect.AdjustBottom( -4 );
1137 aInRect.AdjustLeft(4 );
1138 aInRect.AdjustRight( -4 );
1140 // prepare single line hint (needed on mac to decide between normal push button and
1141 // rectangular bevel button look)
1142 Size aFontSize(Application::GetSettings().GetStyleSettings().GetPushButtonFont().GetFontSize());
1143 aFontSize = rRenderContext.LogicToPixel(aFontSize, MapMode(MapUnit::MapPoint));
1144 Size aInRectSize(rRenderContext.LogicToPixel(Size(aInRect.GetWidth(), aInRect.GetHeight())));
1145 aControlValue.mbSingleLine = (aInRectSize.Height() < 2 * aFontSize.Height());
1147 if (!aControlValue.m_bFlatButton || (nState & ControlState::ROLLOVER) || (nState & ControlState::PRESSED)
1148 || (HasFocus() && mpWindowImpl->mbUseNativeFocus
1149 && !IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus)))
1151 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion, nState,
1152 aControlValue, OUString() /*PushButton::GetText()*/);
1154 else
1156 bNativeOK = true;
1159 // draw content using the same aInRect as non-native VCL would do
1160 ImplDrawPushButtonContent(&rRenderContext, SystemTextColorFlags::NONE,
1161 aInRect, bDrawMenuSep, nButtonStyle);
1163 if (HasFocus())
1164 ShowFocus(ImplGetFocusRect());
1167 if (bNativeOK)
1168 return;
1170 // draw PushButtonFrame, aInRect has content size afterwards
1171 if (GetStyle() & WB_FLATBUTTON)
1173 tools::Rectangle aTempRect(aInRect);
1174 ImplDrawPushButtonFrame(rRenderContext, aTempRect, nButtonStyle);
1175 aInRect.AdjustLeft(2 );
1176 aInRect.AdjustTop(2 );
1177 aInRect.AdjustRight( -2 );
1178 aInRect.AdjustBottom( -2 );
1180 else
1182 ImplDrawPushButtonFrame(rRenderContext, aInRect, nButtonStyle);
1185 // draw content
1186 ImplDrawPushButtonContent(&rRenderContext, SystemTextColorFlags::NONE, aInRect, bDrawMenuSep, nButtonStyle);
1188 if (HasFocus())
1190 ShowFocus(ImplGetFocusRect());
1194 void PushButton::ImplSetDefButton( bool bSet )
1196 Size aSize( GetSizePixel() );
1197 Point aPos( GetPosPixel() );
1198 int dLeft(0), dRight(0), dTop(0), dBottom(0);
1199 bool bSetPos = false;
1201 if ( IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
1203 tools::Rectangle aBound, aCont;
1204 tools::Rectangle aCtrlRegion( 0, 0, 80, 20 ); // use a constant size to avoid accumulating
1205 // will not work if the theme has dynamic adornment sizes
1206 ImplControlValue aControlValue;
1208 // get native size of a 'default' button
1209 // and adjust the VCL button if more space for adornment is required
1210 if( GetNativeControlRegion( ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion,
1211 ControlState::DEFAULT|ControlState::ENABLED,
1212 aControlValue,
1213 aBound, aCont ) )
1215 dLeft = aCont.Left() - aBound.Left();
1216 dTop = aCont.Top() - aBound.Top();
1217 dRight = aBound.Right() - aCont.Right();
1218 dBottom = aBound.Bottom() - aCont.Bottom();
1219 bSetPos = dLeft || dTop || dRight || dBottom;
1223 if ( bSet )
1225 if( !(GetButtonState() & DrawButtonFlags::Default) && bSetPos )
1227 // adjust pos/size when toggling from non-default to default
1228 aPos.Move(-dLeft, -dTop);
1229 aSize.AdjustWidth(dLeft + dRight );
1230 aSize.AdjustHeight(dTop + dBottom );
1232 GetButtonState() |= DrawButtonFlags::Default;
1234 else
1236 if( (GetButtonState() & DrawButtonFlags::Default) && bSetPos )
1238 // adjust pos/size when toggling from default to non-default
1239 aPos.Move(dLeft, dTop);
1240 aSize.AdjustWidth( -(dLeft + dRight) );
1241 aSize.AdjustHeight( -(dTop + dBottom) );
1243 GetButtonState() &= ~DrawButtonFlags::Default;
1245 if( bSetPos )
1246 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
1248 Invalidate();
1251 bool PushButton::ImplIsDefButton() const
1253 return bool(GetButtonState() & DrawButtonFlags::Default);
1256 PushButton::PushButton( WindowType nType ) :
1257 Button( nType )
1259 ImplInitPushButtonData();
1262 PushButton::PushButton( vcl::Window* pParent, WinBits nStyle ) :
1263 Button( WindowType::PUSHBUTTON )
1265 ImplInitPushButtonData();
1266 ImplInit( pParent, nStyle );
1269 void PushButton::MouseButtonDown( const MouseEvent& rMEvt )
1271 if ( !(rMEvt.IsLeft() &&
1272 ImplHitTestPushButton( this, rMEvt.GetPosPixel() )) )
1273 return;
1275 StartTrackingFlags nTrackFlags = StartTrackingFlags::NONE;
1277 if ( ( GetStyle() & WB_REPEAT ) &&
1278 ! ( GetStyle() & WB_TOGGLE ) )
1279 nTrackFlags |= StartTrackingFlags::ButtonRepeat;
1281 GetButtonState() |= DrawButtonFlags::Pressed;
1282 Invalidate();
1283 StartTracking( nTrackFlags );
1285 if ( nTrackFlags & StartTrackingFlags::ButtonRepeat )
1286 Click();
1289 void PushButton::Tracking( const TrackingEvent& rTEvt )
1291 if ( rTEvt.IsTrackingEnded() )
1293 if ( GetButtonState() & DrawButtonFlags::Pressed )
1295 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
1296 GrabFocus();
1298 if ( GetStyle() & WB_TOGGLE )
1300 // Don't toggle, when aborted
1301 if ( !rTEvt.IsTrackingCanceled() )
1303 if ( IsChecked() )
1305 Check( false );
1306 GetButtonState() &= ~DrawButtonFlags::Pressed;
1308 else
1309 Check();
1312 else
1313 GetButtonState() &= ~DrawButtonFlags::Pressed;
1315 Invalidate();
1317 // do not call Click handler if aborted
1318 if ( !rTEvt.IsTrackingCanceled() )
1320 if ( ! ( GetStyle() & WB_REPEAT ) || ( GetStyle() & WB_TOGGLE ) )
1321 Click();
1325 else
1327 if ( ImplHitTestPushButton( this, rTEvt.GetMouseEvent().GetPosPixel() ) )
1329 if ( GetButtonState() & DrawButtonFlags::Pressed )
1331 if ( rTEvt.IsTrackingRepeat() && (GetStyle() & WB_REPEAT) &&
1332 ! ( GetStyle() & WB_TOGGLE ) )
1333 Click();
1335 else
1337 GetButtonState() |= DrawButtonFlags::Pressed;
1338 Invalidate();
1341 else
1343 if ( GetButtonState() & DrawButtonFlags::Pressed )
1345 GetButtonState() &= ~DrawButtonFlags::Pressed;
1346 Invalidate();
1352 void PushButton::KeyInput( const KeyEvent& rKEvt )
1354 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1356 if ( !aKeyCode.GetModifier() &&
1357 ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
1359 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
1361 GetButtonState() |= DrawButtonFlags::Pressed;
1362 Invalidate();
1365 if ( ( GetStyle() & WB_REPEAT ) &&
1366 ! ( GetStyle() & WB_TOGGLE ) )
1367 Click();
1369 else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
1371 GetButtonState() &= ~DrawButtonFlags::Pressed;
1372 Invalidate();
1374 else
1375 Button::KeyInput( rKEvt );
1378 void PushButton::KeyUp( const KeyEvent& rKEvt )
1380 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1382 if ( (GetButtonState() & DrawButtonFlags::Pressed) &&
1383 ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
1385 if ( GetStyle() & WB_TOGGLE )
1387 if ( IsChecked() )
1389 Check( false );
1390 GetButtonState() &= ~DrawButtonFlags::Pressed;
1392 else
1393 Check();
1395 Toggle();
1397 else
1398 GetButtonState() &= ~DrawButtonFlags::Pressed;
1400 Invalidate();
1402 if ( !( GetStyle() & WB_REPEAT ) || ( GetStyle() & WB_TOGGLE ) )
1403 Click();
1405 else
1406 Button::KeyUp( rKEvt );
1409 void PushButton::FillLayoutData() const
1411 mxLayoutData.emplace();
1412 const_cast<PushButton*>(this)->Invalidate();
1415 void PushButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1417 const Image& rCustomButtonImage = GetCustomButtonImage();
1418 if (!!rCustomButtonImage)
1420 rRenderContext.DrawImage(Point(0, 0), rCustomButtonImage);
1421 return;
1423 ImplDrawPushButton(rRenderContext);
1426 void PushButton::Draw( OutputDevice* pDev, const Point& rPos,
1427 SystemTextColorFlags nFlags )
1429 Point aPos = pDev->LogicToPixel( rPos );
1430 Size aSize = GetSizePixel();
1431 tools::Rectangle aRect( aPos, aSize );
1432 vcl::Font aFont = GetDrawPixelFont( pDev );
1434 pDev->Push();
1435 pDev->SetMapMode();
1436 pDev->SetFont( aFont );
1438 std::optional<StyleSettings> oOrigDevStyleSettings;
1440 if ( nFlags & SystemTextColorFlags::Mono )
1442 pDev->SetTextColor( COL_BLACK );
1444 else
1446 pDev->SetTextColor( GetTextColor() );
1447 // DecoView uses the FaceColor...
1448 AllSettings aSettings = pDev->GetSettings();
1449 StyleSettings aStyleSettings = aSettings.GetStyleSettings();
1450 oOrigDevStyleSettings = aStyleSettings;
1451 if ( IsControlBackground() )
1452 aStyleSettings.SetFaceColor( GetControlBackground() );
1453 else
1454 aStyleSettings.SetFaceColor( GetSettings().GetStyleSettings().GetFaceColor() );
1455 aSettings.SetStyleSettings( aStyleSettings );
1456 pDev->OutputDevice::SetSettings( aSettings );
1458 pDev->SetTextFillColor();
1460 DecorationView aDecoView( pDev );
1461 DrawButtonFlags nButtonStyle = DrawButtonFlags::NONE;
1462 if ( nFlags & SystemTextColorFlags::Mono )
1463 nButtonStyle |= DrawButtonFlags::Mono;
1464 if ( IsChecked() )
1465 nButtonStyle |= DrawButtonFlags::Checked;
1466 aRect = aDecoView.DrawButton( aRect, nButtonStyle );
1468 ImplDrawPushButtonContent( pDev, nFlags, aRect, true, nButtonStyle );
1470 // restore original settings (which are not affected by Push/Pop) after
1471 // finished drawing
1472 if (oOrigDevStyleSettings)
1474 AllSettings aSettings = pDev->GetSettings();
1475 aSettings.SetStyleSettings(*oOrigDevStyleSettings);
1476 pDev->OutputDevice::SetSettings( aSettings );
1479 pDev->Pop();
1482 void PushButton::Resize()
1484 Control::Resize();
1485 Invalidate();
1488 void PushButton::GetFocus()
1490 ShowFocus( ImplGetFocusRect() );
1491 SetInputContext( InputContext( GetFont() ) );
1492 Button::GetFocus();
1495 void PushButton::LoseFocus()
1497 EndSelection();
1498 HideFocus();
1499 Button::LoseFocus();
1502 void PushButton::StateChanged( StateChangedType nType )
1504 Button::StateChanged( nType );
1506 if ( (nType == StateChangedType::Enable) ||
1507 (nType == StateChangedType::Text) ||
1508 (nType == StateChangedType::Data) ||
1509 (nType == StateChangedType::State) ||
1510 (nType == StateChangedType::UpdateMode) )
1512 if ( IsReallyVisible() && IsUpdateMode() )
1513 Invalidate();
1515 else if ( nType == StateChangedType::Style )
1517 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
1519 bool bIsDefButton = ( GetStyle() & WB_DEFBUTTON ) != 0;
1520 bool bWasDefButton = ( GetPrevStyle() & WB_DEFBUTTON ) != 0;
1521 if ( bIsDefButton != bWasDefButton )
1522 ImplSetDefButton( bIsDefButton );
1524 if ( IsReallyVisible() && IsUpdateMode() )
1526 if ( (GetPrevStyle() & PUSHBUTTON_VIEW_STYLE) !=
1527 (GetStyle() & PUSHBUTTON_VIEW_STYLE) )
1528 Invalidate();
1531 else if ( (nType == StateChangedType::Zoom) ||
1532 (nType == StateChangedType::ControlFont) )
1534 ImplInitSettings( false );
1535 Invalidate();
1537 else if ( nType == StateChangedType::ControlForeground )
1539 ImplInitSettings( false );
1540 Invalidate();
1542 else if ( nType == StateChangedType::ControlBackground )
1544 ImplInitSettings( true );
1545 Invalidate();
1549 void PushButton::DataChanged( const DataChangedEvent& rDCEvt )
1551 Button::DataChanged( rDCEvt );
1553 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1554 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1555 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1556 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1558 ImplInitSettings( true );
1559 Invalidate();
1563 bool PushButton::PreNotify( NotifyEvent& rNEvt )
1565 if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
1567 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
1568 if( pMouseEvt && (pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow()) )
1570 // trigger redraw as mouse over state has changed
1572 // TODO: move this to Window class or make it a member !!!
1573 ControlType aCtrlType = ControlType::Generic;
1574 switch( GetParent()->GetType() )
1576 case WindowType::LISTBOX:
1577 case WindowType::MULTILISTBOX:
1578 case WindowType::TREELISTBOX:
1579 aCtrlType = ControlType::Listbox;
1580 break;
1582 case WindowType::COMBOBOX:
1583 case WindowType::PATTERNBOX:
1584 case WindowType::NUMERICBOX:
1585 case WindowType::METRICBOX:
1586 case WindowType::CURRENCYBOX:
1587 case WindowType::DATEBOX:
1588 case WindowType::TIMEBOX:
1589 case WindowType::LONGCURRENCYBOX:
1590 aCtrlType = ControlType::Combobox;
1591 break;
1592 default:
1593 break;
1596 bool bDropDown = ( IsSymbol() && (GetSymbol()==SymbolType::SPIN_DOWN) && GetText().isEmpty() );
1598 if( bDropDown && GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::Entire) &&
1599 !GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::ButtonDown) )
1601 vcl::Window *pBorder = GetParent()->GetWindow( GetWindowType::Border );
1602 if(aCtrlType == ControlType::Combobox)
1604 // only paint the button part to avoid flickering of the combobox text
1605 tools::Rectangle aClipRect( Point(), GetOutputSizePixel() );
1606 aClipRect.SetPos(pBorder->ScreenToOutputPixel(OutputToScreenPixel(aClipRect.TopLeft())));
1607 pBorder->Invalidate( aClipRect );
1609 else
1611 pBorder->Invalidate( InvalidateFlags::NoErase );
1614 else if( (GetStyle() & WB_FLATBUTTON) ||
1615 IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
1617 Invalidate();
1622 return Button::PreNotify(rNEvt);
1625 void PushButton::Toggle()
1627 ImplCallEventListenersAndHandler( VclEventId::PushbuttonToggle, nullptr );
1630 void PushButton::SetSymbol( SymbolType eSymbol )
1632 if ( meSymbol != eSymbol )
1634 meSymbol = eSymbol;
1635 CompatStateChanged( StateChangedType::Data );
1639 void PushButton::SetSymbolAlign( SymbolAlign eAlign )
1641 ImplSetSymbolAlign( eAlign );
1644 void PushButton::SetDropDown( PushButtonDropdownStyle nStyle )
1646 if ( mnDDStyle != nStyle )
1648 mnDDStyle = nStyle;
1649 CompatStateChanged( StateChangedType::Data );
1653 void PushButton::SetState( TriState eState )
1655 if ( meState == eState )
1656 return;
1658 meState = eState;
1659 if ( meState == TRISTATE_FALSE )
1660 GetButtonState() &= ~DrawButtonFlags(DrawButtonFlags::Checked | DrawButtonFlags::DontKnow);
1661 else if ( meState == TRISTATE_TRUE )
1663 GetButtonState() &= ~DrawButtonFlags::DontKnow;
1664 GetButtonState() |= DrawButtonFlags::Checked;
1666 else // TRISTATE_INDET
1668 GetButtonState() &= ~DrawButtonFlags::Checked;
1669 GetButtonState() |= DrawButtonFlags::DontKnow;
1672 CompatStateChanged( StateChangedType::State );
1673 Toggle();
1676 void PushButton::statusChanged(const css::frame::FeatureStateEvent& rEvent)
1678 Button::statusChanged(rEvent);
1679 if (rEvent.State.has<bool>())
1680 SetPressed(rEvent.State.get<bool>());
1683 void PushButton::SetPressed( bool bPressed )
1685 if ( mbPressed != bPressed )
1687 mbPressed = bPressed;
1688 CompatStateChanged( StateChangedType::Data );
1692 void PushButton::EndSelection()
1694 EndTracking( TrackingEventFlags::Cancel );
1695 if ( !isDisposed() &&
1696 GetButtonState() & DrawButtonFlags::Pressed )
1698 GetButtonState() &= ~DrawButtonFlags::Pressed;
1699 if ( !mbPressed )
1700 Invalidate();
1704 Size PushButton::CalcMinimumSize() const
1706 Size aSize;
1708 if ( IsSymbol() )
1710 if ( IsSmallSymbol ())
1711 aSize = Size( 16, 12 );
1712 else
1713 aSize = Size( 26, 24 );
1715 else if ( Button::HasImage() )
1716 aSize = GetModeImage().GetSizePixel();
1717 if( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
1718 mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
1720 tools::Long nSymbolSize = GetTextHeight() / 2 + 1;
1721 aSize.AdjustWidth(2*nSymbolSize );
1723 if (!PushButton::GetText().isEmpty())
1725 Size textSize = GetTextRect( tools::Rectangle( Point(), Size( 0x7fffffff, 0x7fffffff ) ),
1726 PushButton::GetText(), ImplGetTextStyle( SystemTextColorFlags::NONE ) ).GetSize();
1728 tools::Long nTextHeight = textSize.Height() * 1.15;
1730 ImageAlign eImageAlign = GetImageAlign();
1731 // tdf#142337 only considering the simple top/bottom/left/right possibilities
1732 if (eImageAlign == ImageAlign::Top || eImageAlign == ImageAlign::Bottom)
1734 aSize.AdjustHeight(nTextHeight);
1735 aSize.setWidth(std::max(aSize.Width(), textSize.Width()));
1737 else
1739 aSize.AdjustWidth(textSize.Width());
1740 aSize.setHeight(std::max(aSize.Height(), nTextHeight));
1744 // cf. ImplDrawPushButton ...
1745 if( (GetStyle() & WB_SMALLSTYLE) == 0 )
1747 aSize.AdjustWidth(24 );
1748 aSize.AdjustHeight(12 );
1751 return CalcWindowSize( aSize );
1754 Size PushButton::GetOptimalSize() const
1756 return CalcMinimumSize();
1759 bool PushButton::set_property(const OUString &rKey, const OUString &rValue)
1761 if (rKey == "has-default")
1763 WinBits nBits = GetStyle();
1764 nBits &= ~WB_DEFBUTTON;
1765 if (toBool(rValue))
1766 nBits |= WB_DEFBUTTON;
1767 SetStyle(nBits);
1769 else
1770 return Button::set_property(rKey, rValue);
1771 return true;
1774 void PushButton::ShowFocus(const tools::Rectangle& rRect)
1776 if (IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
1778 PushButtonValue aControlValue;
1779 aControlValue.mbIsAction = isAction();
1780 tools::Rectangle aInRect(Point(), GetOutputSizePixel());
1781 GetOutDev()->DrawNativeControl(ControlType::Pushbutton, ControlPart::Focus, aInRect,
1782 ControlState::FOCUSED, aControlValue, OUString());
1784 Button::ShowFocus(rRect);
1787 void OKButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1789 set_id(u"ok"_ustr);
1790 PushButton::ImplInit( pParent, nStyle );
1792 SetText( GetStandardText( StandardButtonType::OK ) );
1795 OKButton::OKButton( vcl::Window* pParent, WinBits nStyle ) :
1796 PushButton( WindowType::OKBUTTON )
1798 ImplInit( pParent, nStyle );
1801 void OKButton::Click()
1803 // close parent if no link set
1804 if ( !GetClickHdl() )
1806 vcl::Window* pParent = getNonLayoutParent(this);
1807 if ( pParent->IsSystemWindow() )
1809 if ( pParent->IsDialog() )
1811 VclPtr<Dialog> xParent( static_cast<Dialog*>(pParent) );
1812 if ( xParent->IsInExecute() )
1813 xParent->EndDialog( RET_OK );
1814 // prevent recursive calls
1815 else if ( !xParent->IsInClose() )
1817 if ( pParent->GetStyle() & WB_CLOSEABLE )
1818 xParent->Close();
1821 else
1823 if ( pParent->GetStyle() & WB_CLOSEABLE )
1824 static_cast<SystemWindow*>(pParent)->Close();
1828 else
1830 PushButton::Click();
1834 void CancelButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1836 set_id(u"cancel"_ustr);
1837 PushButton::ImplInit( pParent, nStyle );
1839 SetText( GetStandardText( StandardButtonType::Cancel ) );
1842 CancelButton::CancelButton( vcl::Window* pParent, WinBits nStyle ) :
1843 PushButton( WindowType::CANCELBUTTON )
1845 ImplInit( pParent, nStyle );
1848 void CancelButton::Click()
1850 // close parent if link not set
1851 if ( !GetClickHdl() )
1853 vcl::Window* pParent = getNonLayoutParent(this);
1854 if ( pParent->IsSystemWindow() )
1856 if ( pParent->IsDialog() )
1858 if ( static_cast<Dialog*>(pParent)->IsInExecute() )
1859 static_cast<Dialog*>(pParent)->EndDialog();
1860 // prevent recursive calls
1861 else if ( !static_cast<Dialog*>(pParent)->IsInClose() )
1863 if ( pParent->GetStyle() & WB_CLOSEABLE )
1864 static_cast<Dialog*>(pParent)->Close();
1867 else
1869 if ( pParent->GetStyle() & WB_CLOSEABLE )
1870 static_cast<SystemWindow*>(pParent)->Close();
1874 else
1876 PushButton::Click();
1880 CloseButton::CloseButton( vcl::Window* pParent )
1881 : CancelButton(pParent, 0)
1883 SetText( GetStandardText( StandardButtonType::Close ) );
1886 void HelpButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1888 set_id(u"help"_ustr);
1889 PushButton::ImplInit( pParent, nStyle | WB_NOPOINTERFOCUS );
1891 SetText( GetStandardText( StandardButtonType::Help ) );
1894 HelpButton::HelpButton( vcl::Window* pParent, WinBits nStyle ) :
1895 PushButton( WindowType::HELPBUTTON )
1897 ImplInit( pParent, nStyle );
1900 void HelpButton::Click()
1902 // trigger help if no link set
1903 if ( !GetClickHdl() )
1905 vcl::Window* pFocusWin = Application::GetFocusWindow();
1906 if ( !pFocusWin || comphelper::LibreOfficeKit::isActive() )
1907 pFocusWin = this;
1909 HelpEvent aEvt( pFocusWin->GetPointerPosPixel(), HelpEventMode::CONTEXT );
1910 pFocusWin->RequestHelp( aEvt );
1912 PushButton::Click();
1915 void HelpButton::StateChanged( StateChangedType nStateChange )
1917 // Hide when we have no help URL.
1918 if (comphelper::LibreOfficeKit::isActive() &&
1919 officecfg::Office::Common::Help::HelpRootURL::get().isEmpty())
1920 Hide();
1921 else
1922 PushButton::StateChanged(nStateChange);
1925 void RadioButton::ImplInitRadioButtonData()
1927 mbChecked = false;
1928 mbRadioCheck = true;
1929 mbStateChanged = false;
1932 void RadioButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1934 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
1935 Button::ImplInit( pParent, nStyle, nullptr );
1937 ImplInitSettings( true );
1940 WinBits RadioButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle ) const
1942 if ( !(nStyle & WB_NOGROUP) &&
1943 (!pPrevWindow || (pPrevWindow->GetType() != WindowType::RADIOBUTTON)) )
1944 nStyle |= WB_GROUP;
1945 if ( !(nStyle & WB_NOTABSTOP) )
1947 if ( IsChecked() )
1948 nStyle |= WB_TABSTOP;
1949 else
1950 nStyle &= ~WB_TABSTOP;
1953 return nStyle;
1956 const vcl::Font& RadioButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
1958 return _rStyle.GetRadioCheckFont();
1961 const Color& RadioButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
1963 return _rStyle.GetRadioCheckTextColor();
1966 void RadioButton::ImplInitSettings( bool bBackground )
1968 Button::ImplInitSettings();
1970 if ( !bBackground )
1971 return;
1973 vcl::Window* pParent = GetParent();
1974 if ( !IsControlBackground() &&
1975 (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) ) )
1977 EnableChildTransparentMode();
1978 SetParentClipMode( ParentClipMode::NoClip );
1979 SetPaintTransparent( true );
1980 SetBackground();
1981 if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
1982 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
1984 else
1986 EnableChildTransparentMode( false );
1987 SetParentClipMode();
1988 SetPaintTransparent( false );
1990 if ( IsControlBackground() )
1991 SetBackground( GetControlBackground() );
1992 else
1993 SetBackground( pParent->GetBackground() );
1997 void RadioButton::ImplDrawRadioButtonState(vcl::RenderContext& rRenderContext)
1999 bool bNativeOK = false;
2001 // no native drawing for image radio buttons
2002 if (!maImage && rRenderContext.IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire))
2004 ImplControlValue aControlValue( mbChecked ? ButtonValue::On : ButtonValue::Off );
2005 tools::Rectangle aCtrlRect(maStateRect.TopLeft(), maStateRect.GetSize());
2006 ControlState nState = ControlState::NONE;
2008 if (GetButtonState() & DrawButtonFlags::Pressed)
2009 nState |= ControlState::PRESSED;
2010 if (HasFocus())
2011 nState |= ControlState::FOCUSED;
2012 if (GetButtonState() & DrawButtonFlags::Default)
2013 nState |= ControlState::DEFAULT;
2014 if (IsEnabled())
2015 nState |= ControlState::ENABLED;
2017 if (IsMouseOver() && maMouseRect.Contains(GetPointerPosPixel()))
2018 nState |= ControlState::ROLLOVER;
2020 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Radiobutton, ControlPart::Entire, aCtrlRect,
2021 nState, aControlValue, OUString());
2024 if (bNativeOK)
2025 return;
2027 if (!maImage)
2029 DrawButtonFlags nStyle = GetButtonState();
2030 if (!IsEnabled())
2031 nStyle |= DrawButtonFlags::Disabled;
2032 if (mbChecked)
2033 nStyle |= DrawButtonFlags::Checked;
2034 Image aImage = GetRadioImage(rRenderContext.GetSettings(), nStyle);
2035 if (IsZoom())
2036 rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
2037 else
2038 rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
2040 else
2042 HideFocus();
2044 DecorationView aDecoView(&rRenderContext);
2045 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
2046 tools::Rectangle aImageRect = maStateRect;
2047 Size aImageSize = maImage.GetSizePixel();
2048 bool bEnabled = IsEnabled();
2050 aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
2051 aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
2053 aImageRect.AdjustLeft( 1 );
2054 aImageRect.AdjustTop( 1 );
2055 aImageRect.AdjustRight( -1 );
2056 aImageRect.AdjustBottom( -1 );
2058 // display border and selection status
2059 aImageRect = aDecoView.DrawFrame(aImageRect, DrawFrameStyle::DoubleIn);
2060 if ((GetButtonState() & DrawButtonFlags::Pressed) || !bEnabled)
2061 rRenderContext.SetFillColor( rStyleSettings.GetFaceColor());
2062 else
2063 rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
2064 rRenderContext.SetLineColor();
2065 rRenderContext.DrawRect(aImageRect);
2067 // display image
2068 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
2069 if (!bEnabled)
2070 nImageStyle |= DrawImageFlags::Disable;
2072 Image* pImage = &maImage;
2074 Point aImagePos(aImageRect.TopLeft());
2075 aImagePos.AdjustX((aImageRect.GetWidth() - aImageSize.Width()) / 2 );
2076 aImagePos.AdjustY((aImageRect.GetHeight() - aImageSize.Height()) / 2 );
2077 if (IsZoom())
2078 rRenderContext.DrawImage(aImagePos, aImageSize, *pImage, nImageStyle);
2079 else
2080 rRenderContext.DrawImage(aImagePos, *pImage, nImageStyle);
2082 aImageRect.AdjustLeft( 1 );
2083 aImageRect.AdjustTop( 1 );
2084 aImageRect.AdjustRight( -1 );
2085 aImageRect.AdjustBottom( -1 );
2087 ImplSetFocusRect(aImageRect);
2089 if (mbChecked)
2091 rRenderContext.SetLineColor(rStyleSettings.GetHighlightColor());
2092 rRenderContext.SetFillColor();
2093 if ((aImageSize.Width() >= 20) || (aImageSize.Height() >= 20))
2095 aImageRect.AdjustLeft( 1 );
2096 aImageRect.AdjustTop( 1 );
2097 aImageRect.AdjustRight( -1 );
2098 aImageRect.AdjustBottom( -1 );
2100 rRenderContext.DrawRect(aImageRect);
2101 aImageRect.AdjustLeft( 1 );
2102 aImageRect.AdjustTop( 1 );
2103 aImageRect.AdjustRight( -1 );
2104 aImageRect.AdjustBottom( -1 );
2105 rRenderContext.DrawRect(aImageRect);
2108 if (HasFocus())
2109 ShowFocus(ImplGetFocusRect());
2113 // for drawing RadioButton or CheckButton that has Text and/or Image
2114 void Button::ImplDrawRadioCheck(OutputDevice* pDev, WinBits nWinStyle, SystemTextColorFlags nSystemTextColorFlags,
2115 const Point& rPos, const Size& rSize,
2116 const Size& rImageSize, tools::Rectangle& rStateRect,
2117 tools::Rectangle& rMouseRect)
2119 DrawTextFlags nTextStyle = Button::ImplGetTextStyle( nWinStyle, nSystemTextColorFlags );
2121 const tools::Long nImageSep = GetDrawPixel( pDev, ImplGetImageToTextDistance() );
2122 Size aSize( rSize );
2123 Point aPos( rPos );
2124 aPos.AdjustX(rImageSize.Width() + nImageSep );
2126 // tdf#141761 Old (convenience?) adjustment of width may lead to empty
2127 // or negative(!) Size, that needs to be avoided. The coordinate context
2128 // is pixel-oriented (all Paints of Controls are, historically), so
2129 // the minimum width should be '1' Pixel.
2130 // Hint: nImageSep is based on Zoom (using Window::CalcZoom) and
2131 // MapModes (using Window::GetDrawPixel) - so potentially a wide range
2132 // of unpredictable values is possible
2133 const tools::Long nWidthAdjust(rImageSize.Width() + nImageSep);
2134 aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
2136 // if the text rect height is smaller than the height of the image
2137 // then for single lines the default should be centered text
2138 if( (nWinStyle & (WB_TOP|WB_VCENTER|WB_BOTTOM)) == 0 &&
2139 (rImageSize.Height() > rSize.Height() || ! (nWinStyle & WB_WORDBREAK) ) )
2141 nTextStyle &= ~DrawTextFlags(DrawTextFlags::Top|DrawTextFlags::Bottom);
2142 nTextStyle |= DrawTextFlags::VCenter;
2143 aSize.setHeight( rImageSize.Height() );
2146 ImplDrawAlignedImage( pDev, aPos, aSize, 1, nTextStyle );
2148 rMouseRect = tools::Rectangle( aPos, aSize );
2149 rMouseRect.SetLeft( rPos.X() );
2151 rStateRect.SetLeft( rPos.X() );
2152 rStateRect.SetTop( rMouseRect.Top() );
2154 if ( aSize.Height() > rImageSize.Height() )
2155 rStateRect.AdjustTop(( aSize.Height() - rImageSize.Height() ) / 2 );
2156 else
2158 rStateRect.AdjustTop( -(( rImageSize.Height() - aSize.Height() ) / 2) );
2159 if( rStateRect.Top() < 0 )
2160 rStateRect.SetTop( 0 );
2163 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
2164 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
2166 if ( rStateRect.Bottom() > rMouseRect.Bottom() )
2167 rMouseRect.SetBottom( rStateRect.Bottom() );
2170 void RadioButton::ImplDraw( OutputDevice* pDev, SystemTextColorFlags nSystemTextColorFlags,
2171 const Point& rPos, const Size& rSize,
2172 const Size& rImageSize, tools::Rectangle& rStateRect,
2173 tools::Rectangle& rMouseRect )
2175 WinBits nWinStyle = GetStyle();
2176 OUString aText( GetText() );
2178 pDev->Push( vcl::PushFlags::CLIPREGION );
2179 pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
2181 // no image radio button
2182 if ( !maImage )
2184 if (!aText.isEmpty() || HasImage())
2186 Button::ImplDrawRadioCheck(pDev, nWinStyle, nSystemTextColorFlags,
2187 rPos, rSize, rImageSize,
2188 rStateRect, rMouseRect);
2190 else
2192 rStateRect.SetLeft( rPos.X() );
2193 if ( nWinStyle & WB_VCENTER )
2194 rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
2195 else if ( nWinStyle & WB_BOTTOM )
2196 rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() ); //-1;
2197 else
2198 rStateRect.SetTop( rPos.Y() );
2199 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
2200 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
2201 rMouseRect = rStateRect;
2203 ImplSetFocusRect( rStateRect );
2206 else
2208 bool bTopImage = (nWinStyle & WB_TOP) != 0;
2209 Size aImageSize = maImage.GetSizePixel();
2210 tools::Rectangle aImageRect( rPos, rSize );
2211 tools::Long nTextHeight = pDev->GetTextHeight();
2212 tools::Long nTextWidth = pDev->GetCtrlTextWidth( aText );
2214 // calculate position and sizes
2215 if (!aText.isEmpty())
2217 Size aTmpSize( (aImageSize.Width()+8), (aImageSize.Height()+8) );
2218 if ( bTopImage )
2220 aImageRect.SetLeft( (rSize.Width()-aTmpSize.Width())/2 );
2221 aImageRect.SetTop( (rSize.Height()-(aTmpSize.Height()+nTextHeight+6))/2 );
2223 else
2224 aImageRect.SetTop( (rSize.Height()-aTmpSize.Height())/2 );
2226 aImageRect.SetRight( aImageRect.Left()+aTmpSize.Width() );
2227 aImageRect.SetBottom( aImageRect.Top()+aTmpSize.Height() );
2229 // display text
2230 Point aTxtPos = rPos;
2231 if ( bTopImage )
2233 aTxtPos.AdjustX((rSize.Width()-nTextWidth)/2 );
2234 aTxtPos.AdjustY(aImageRect.Bottom()+6 );
2236 else
2238 aTxtPos.AdjustX(aImageRect.Right()+8 );
2239 aTxtPos.AdjustY((rSize.Height()-nTextHeight)/2 );
2241 pDev->DrawCtrlText( aTxtPos, aText, 0, aText.getLength() );
2244 rMouseRect = aImageRect;
2245 rStateRect = aImageRect;
2248 pDev->Pop();
2251 void RadioButton::ImplDrawRadioButton(vcl::RenderContext& rRenderContext)
2253 HideFocus();
2255 Size aImageSize;
2256 if (!maImage)
2257 aImageSize = ImplGetRadioImageSize();
2258 else
2259 aImageSize = maImage.GetSizePixel();
2261 aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
2262 aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
2264 // Draw control text
2265 ImplDraw(&rRenderContext, SystemTextColorFlags::NONE, Point(), GetOutputSizePixel(),
2266 aImageSize, maStateRect, maMouseRect);
2268 if (!maImage && HasFocus())
2269 ShowFocus(ImplGetFocusRect());
2271 ImplDrawRadioButtonState(rRenderContext);
2274 void RadioButton::group(RadioButton &rOther)
2276 if (&rOther == this)
2277 return;
2279 if (!m_xGroup)
2281 m_xGroup = std::make_shared<std::vector<VclPtr<RadioButton> >>();
2282 m_xGroup->push_back(this);
2285 auto aFind = std::find(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(&rOther));
2286 if (aFind == m_xGroup->end())
2288 m_xGroup->push_back(&rOther);
2290 if (rOther.m_xGroup)
2292 std::vector< VclPtr<RadioButton> > aOthers(rOther.GetRadioButtonGroup(false));
2293 //make all members of the group share the same button group
2294 for (auto const& elem : aOthers)
2296 aFind = std::find(m_xGroup->begin(), m_xGroup->end(), elem);
2297 if (aFind == m_xGroup->end())
2298 m_xGroup->push_back(elem);
2302 //make all members of the group share the same button group
2303 for (VclPtr<RadioButton> const & pButton : *m_xGroup)
2305 pButton->m_xGroup = m_xGroup;
2309 //if this one is checked, uncheck all the others
2310 if (mbChecked)
2311 ImplUncheckAllOther();
2314 std::vector< VclPtr<RadioButton> > RadioButton::GetRadioButtonGroup(bool bIncludeThis) const
2316 if (m_xGroup)
2318 if (bIncludeThis)
2319 return *m_xGroup;
2320 std::vector< VclPtr<RadioButton> > aGroup;
2321 for (VclPtr<RadioButton> const & pRadioButton : *m_xGroup)
2323 if (pRadioButton == this)
2324 continue;
2325 aGroup.push_back(pRadioButton);
2327 return aGroup;
2330 std::vector<VclPtr<RadioButton>> aGroup;
2331 if (mbUsesExplicitGroup)
2332 return aGroup;
2334 //old-school
2336 // go back to first in group;
2337 vcl::Window* pFirst = const_cast<RadioButton*>(this);
2338 while( ( pFirst->GetStyle() & WB_GROUP ) == 0 )
2340 vcl::Window* pWindow = pFirst->GetWindow( GetWindowType::Prev );
2341 if( pWindow )
2342 pFirst = pWindow;
2343 else
2344 break;
2346 // insert radiobuttons up to next group
2349 if( pFirst->GetType() == WindowType::RADIOBUTTON )
2351 if( pFirst != this || bIncludeThis )
2352 aGroup.emplace_back(static_cast<RadioButton*>(pFirst) );
2354 pFirst = pFirst->GetWindow( GetWindowType::Next );
2355 } while( pFirst && ( ( pFirst->GetStyle() & WB_GROUP ) == 0 ) );
2357 return aGroup;
2360 void RadioButton::ImplUncheckAllOther()
2362 mpWindowImpl->mnStyle |= WB_TABSTOP;
2364 std::vector<VclPtr<RadioButton> > aGroup(GetRadioButtonGroup(false));
2365 // iterate over radio button group and checked buttons
2366 for (VclPtr<RadioButton>& pWindow : aGroup)
2368 if ( pWindow->IsChecked() )
2370 pWindow->SetState( false );
2371 if ( pWindow->isDisposed() )
2372 return;
2375 // not inside if clause to always remove wrongly set WB_TABSTOPS
2376 pWindow->mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2380 void RadioButton::ImplCallClick( bool bGrabFocus, GetFocusFlags nFocusFlags )
2382 mbStateChanged = !mbChecked;
2383 mbChecked = true;
2384 mpWindowImpl->mnStyle |= WB_TABSTOP;
2385 Invalidate();
2386 VclPtr<vcl::Window> xWindow = this;
2387 if ( mbRadioCheck )
2388 ImplUncheckAllOther();
2389 if ( xWindow->isDisposed() )
2390 return;
2391 if ( bGrabFocus )
2392 ImplGrabFocus( nFocusFlags );
2393 if ( xWindow->isDisposed() )
2394 return;
2395 if ( mbStateChanged )
2396 Toggle();
2397 if ( xWindow->isDisposed() )
2398 return;
2399 Click();
2400 if ( xWindow->isDisposed() )
2401 return;
2402 mbStateChanged = false;
2405 RadioButton::RadioButton(vcl::Window* pParent, bool bUsesExplicitGroup, WinBits nStyle)
2406 : Button(WindowType::RADIOBUTTON)
2407 , mbUsesExplicitGroup(bUsesExplicitGroup)
2409 ImplInitRadioButtonData();
2410 ImplInit( pParent, nStyle );
2413 RadioButton::~RadioButton()
2415 disposeOnce();
2418 void RadioButton::dispose()
2420 if (m_xGroup)
2422 std::erase(*m_xGroup, VclPtr<RadioButton>(this));
2423 m_xGroup.reset();
2425 Button::dispose();
2428 void RadioButton::MouseButtonDown( const MouseEvent& rMEvt )
2430 if ( rMEvt.IsLeft() && maMouseRect.Contains( rMEvt.GetPosPixel() ) )
2432 GetButtonState() |= DrawButtonFlags::Pressed;
2433 Invalidate();
2434 StartTracking();
2435 return;
2438 Button::MouseButtonDown( rMEvt );
2441 void RadioButton::Tracking( const TrackingEvent& rTEvt )
2443 if ( rTEvt.IsTrackingEnded() )
2445 if ( GetButtonState() & DrawButtonFlags::Pressed )
2447 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
2448 GrabFocus();
2450 GetButtonState() &= ~DrawButtonFlags::Pressed;
2452 // do not call click handler if aborted
2453 if ( !rTEvt.IsTrackingCanceled() )
2454 ImplCallClick();
2455 else
2457 Invalidate();
2461 else
2463 if ( maMouseRect.Contains( rTEvt.GetMouseEvent().GetPosPixel() ) )
2465 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
2467 GetButtonState() |= DrawButtonFlags::Pressed;
2468 Invalidate();
2471 else
2473 if ( GetButtonState() & DrawButtonFlags::Pressed )
2475 GetButtonState() &= ~DrawButtonFlags::Pressed;
2476 Invalidate();
2482 void RadioButton::KeyInput( const KeyEvent& rKEvt )
2484 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
2486 if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
2488 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
2490 GetButtonState() |= DrawButtonFlags::Pressed;
2491 Invalidate();
2494 else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
2496 GetButtonState() &= ~DrawButtonFlags::Pressed;
2497 Invalidate();
2499 else
2500 Button::KeyInput( rKEvt );
2503 void RadioButton::KeyUp( const KeyEvent& rKEvt )
2505 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
2507 if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
2509 GetButtonState() &= ~DrawButtonFlags::Pressed;
2510 ImplCallClick();
2512 else
2513 Button::KeyUp( rKEvt );
2516 void RadioButton::FillLayoutData() const
2518 mxLayoutData.emplace();
2519 const_cast<RadioButton*>(this)->Invalidate();
2522 void RadioButton::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
2524 ImplDrawRadioButton(rRenderContext);
2527 void RadioButton::Draw( OutputDevice* pDev, const Point& rPos,
2528 SystemTextColorFlags nFlags )
2530 if ( !maImage )
2532 MapMode aResMapMode( MapUnit::Map100thMM );
2533 Point aPos = pDev->LogicToPixel( rPos );
2534 Size aSize = GetSizePixel();
2535 Size aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
2536 Size aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
2537 Size aBrd2Size = pDev->LogicToPixel( Size( 60, 60 ), aResMapMode );
2538 vcl::Font aFont = GetDrawPixelFont( pDev );
2539 tools::Rectangle aStateRect;
2540 tools::Rectangle aMouseRect;
2542 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
2543 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
2544 aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
2545 aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
2546 aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
2547 aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
2549 if ( !aBrd1Size.Width() )
2550 aBrd1Size.setWidth( 1 );
2551 if ( !aBrd1Size.Height() )
2552 aBrd1Size.setHeight( 1 );
2553 if ( !aBrd2Size.Width() )
2554 aBrd2Size.setWidth( 1 );
2555 if ( !aBrd2Size.Height() )
2556 aBrd2Size.setHeight( 1 );
2558 pDev->Push();
2559 pDev->SetMapMode();
2560 pDev->SetFont( aFont );
2561 if ( nFlags & SystemTextColorFlags::Mono )
2562 pDev->SetTextColor( COL_BLACK );
2563 else
2564 pDev->SetTextColor( GetTextColor() );
2565 pDev->SetTextFillColor();
2567 ImplDraw( pDev, nFlags, aPos, aSize,
2568 aImageSize, aStateRect, aMouseRect );
2570 Point aCenterPos = aStateRect.Center();
2571 tools::Long nRadX = aImageSize.Width()/2;
2572 tools::Long nRadY = aImageSize.Height()/2;
2574 pDev->SetLineColor();
2575 pDev->SetFillColor( COL_BLACK );
2576 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2577 nRadX -= aBrd1Size.Width();
2578 nRadY -= aBrd1Size.Height();
2579 pDev->SetFillColor( COL_WHITE );
2580 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2581 if ( mbChecked )
2583 nRadX -= aBrd1Size.Width();
2584 nRadY -= aBrd1Size.Height();
2585 if ( !nRadX )
2586 nRadX = 1;
2587 if ( !nRadY )
2588 nRadY = 1;
2589 pDev->SetFillColor( COL_BLACK );
2590 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2593 pDev->Pop();
2595 else
2597 OSL_FAIL( "RadioButton::Draw() - not implemented for RadioButton with Image" );
2601 void RadioButton::Resize()
2603 Control::Resize();
2604 Invalidate();
2607 void RadioButton::GetFocus()
2609 ShowFocus( ImplGetFocusRect() );
2610 SetInputContext( InputContext( GetFont() ) );
2611 Button::GetFocus();
2614 void RadioButton::LoseFocus()
2616 if ( GetButtonState() & DrawButtonFlags::Pressed )
2618 GetButtonState() &= ~DrawButtonFlags::Pressed;
2619 Invalidate();
2622 HideFocus();
2623 Button::LoseFocus();
2626 void RadioButton::StateChanged( StateChangedType nType )
2628 Button::StateChanged( nType );
2630 if ( nType == StateChangedType::State )
2632 if ( IsReallyVisible() && IsUpdateMode() )
2633 Invalidate( maStateRect );
2635 else if ( (nType == StateChangedType::Enable) ||
2636 (nType == StateChangedType::Text) ||
2637 (nType == StateChangedType::Data) ||
2638 (nType == StateChangedType::UpdateMode) )
2640 if ( IsUpdateMode() )
2641 Invalidate();
2643 else if ( nType == StateChangedType::Style )
2645 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
2647 if ( (GetPrevStyle() & RADIOBUTTON_VIEW_STYLE) !=
2648 (GetStyle() & RADIOBUTTON_VIEW_STYLE) )
2650 if ( IsUpdateMode() )
2651 Invalidate();
2654 else if ( (nType == StateChangedType::Zoom) ||
2655 (nType == StateChangedType::ControlFont) )
2657 ImplInitSettings( false );
2658 Invalidate();
2660 else if ( nType == StateChangedType::ControlForeground )
2662 ImplInitSettings( false );
2663 Invalidate();
2665 else if ( nType == StateChangedType::ControlBackground )
2667 ImplInitSettings( true );
2668 Invalidate();
2672 void RadioButton::DataChanged( const DataChangedEvent& rDCEvt )
2674 Button::DataChanged( rDCEvt );
2676 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2677 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2678 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2679 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2681 ImplInitSettings( true );
2682 Invalidate();
2686 bool RadioButton::PreNotify( NotifyEvent& rNEvt )
2688 if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
2690 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
2691 if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
2693 // trigger redraw if mouse over state has changed
2694 if( IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire) )
2696 if (maMouseRect.Contains(GetPointerPosPixel()) != maMouseRect.Contains(GetLastPointerPosPixel()) ||
2697 pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
2699 Invalidate( maStateRect );
2705 return Button::PreNotify(rNEvt);
2708 void RadioButton::Toggle()
2710 ImplCallEventListenersAndHandler( VclEventId::RadiobuttonToggle, [this] () { maToggleHdl.Call(*this); } );
2713 void RadioButton::SetModeRadioImage( const Image& rImage )
2715 if ( rImage != maImage )
2717 maImage = rImage;
2718 CompatStateChanged( StateChangedType::Data );
2719 queue_resize();
2724 void RadioButton::SetState( bool bCheck )
2726 // carry the TabStop flag along correctly
2727 if ( bCheck )
2728 mpWindowImpl->mnStyle |= WB_TABSTOP;
2729 else
2730 mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2732 if ( mbChecked != bCheck )
2734 mbChecked = bCheck;
2735 CompatStateChanged( StateChangedType::State );
2736 Toggle();
2740 bool RadioButton::set_property(const OUString &rKey, const OUString &rValue)
2742 if (rKey == "active")
2743 SetState(toBool(rValue));
2744 else if (rKey == "image-position")
2746 WinBits nBits = GetStyle();
2747 if (rValue == "left")
2749 nBits &= ~(WB_CENTER | WB_RIGHT);
2750 nBits |= WB_LEFT;
2752 else if (rValue == "right")
2754 nBits &= ~(WB_CENTER | WB_LEFT);
2755 nBits |= WB_RIGHT;
2757 else if (rValue == "top")
2759 nBits &= ~(WB_VCENTER | WB_BOTTOM);
2760 nBits |= WB_TOP;
2762 else if (rValue == "bottom")
2764 nBits &= ~(WB_VCENTER | WB_TOP);
2765 nBits |= WB_BOTTOM;
2767 //It's rather mad to have to set these bits when there is the other
2768 //image align. Looks like e.g. the radiobuttons etc weren't converted
2769 //over to image align fully.
2770 SetStyle(nBits);
2771 //Deliberate to set the sane ImageAlign property
2772 return Button::set_property(rKey, rValue);
2774 else
2775 return Button::set_property(rKey, rValue);
2776 return true;
2779 void RadioButton::Check( bool bCheck )
2781 // TabStop-Flag richtig mitfuehren
2782 if ( bCheck )
2783 mpWindowImpl->mnStyle |= WB_TABSTOP;
2784 else
2785 mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2787 if ( mbChecked == bCheck )
2788 return;
2790 mbChecked = bCheck;
2791 VclPtr<vcl::Window> xWindow = this;
2792 CompatStateChanged( StateChangedType::State );
2793 if ( xWindow->isDisposed() )
2794 return;
2795 if ( bCheck && mbRadioCheck )
2796 ImplUncheckAllOther();
2797 if ( xWindow->isDisposed() )
2798 return;
2799 Toggle();
2802 tools::Long Button::ImplGetImageToTextDistance() const
2804 // 4 pixels, but take zoom into account, so the text doesn't "jump" relative to surrounding elements,
2805 // which might have been aligned with the text of the check box
2806 return CalcZoom( 4 );
2809 Size RadioButton::ImplGetRadioImageSize() const
2811 Size aSize;
2812 bool bDefaultSize = true;
2813 if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
2815 ImplControlValue aControlValue;
2816 tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
2817 tools::Rectangle aBoundingRgn, aContentRgn;
2819 // get native size of a radio button
2820 if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
2821 ControlState::DEFAULT|ControlState::ENABLED,
2822 aControlValue,
2823 aBoundingRgn, aContentRgn ) )
2825 aSize = aContentRgn.GetSize();
2826 bDefaultSize = false;
2829 if( bDefaultSize )
2830 aSize = GetRadioImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
2831 return aSize;
2834 static void LoadThemedImageList(const StyleSettings &rStyleSettings,
2835 std::vector<Image>& rList, const std::vector<OUString> &rResources)
2837 Color aColorAry1[6];
2838 Color aColorAry2[6];
2839 aColorAry1[0] = Color( 0xC0, 0xC0, 0xC0 );
2840 aColorAry1[1] = Color( 0xFF, 0xFF, 0x00 );
2841 aColorAry1[2] = Color( 0xFF, 0xFF, 0xFF );
2842 aColorAry1[3] = Color( 0x80, 0x80, 0x80 );
2843 aColorAry1[4] = Color( 0x00, 0x00, 0x00 );
2844 aColorAry1[5] = Color( 0x00, 0xFF, 0x00 );
2845 aColorAry2[0] = rStyleSettings.GetFaceColor();
2846 aColorAry2[1] = rStyleSettings.GetWindowColor();
2847 aColorAry2[2] = rStyleSettings.GetLightColor();
2848 aColorAry2[3] = rStyleSettings.GetShadowColor();
2849 aColorAry2[4] = rStyleSettings.GetDarkShadowColor();
2850 aColorAry2[5] = rStyleSettings.GetWindowTextColor();
2852 static_assert( sizeof(aColorAry1) == sizeof(aColorAry2), "aColorAry1 must match aColorAry2" );
2854 for (const auto &a : rResources)
2856 BitmapEx aBmpEx(a);
2857 aBmpEx.Replace(aColorAry1, aColorAry2, SAL_N_ELEMENTS(aColorAry1));
2858 rList.emplace_back(aBmpEx);
2862 Image RadioButton::GetRadioImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
2864 ImplSVData* pSVData = ImplGetSVData();
2865 const StyleSettings& rStyleSettings = rSettings.GetStyleSettings();
2866 sal_uInt16 nStyle = 0;
2868 if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
2869 nStyle = STYLE_RADIOBUTTON_MONO;
2871 if ( pSVData->maCtrlData.maRadioImgList.empty() ||
2872 (pSVData->maCtrlData.mnRadioStyle != nStyle) ||
2873 (pSVData->maCtrlData.mnLastRadioFColor != rStyleSettings.GetFaceColor()) ||
2874 (pSVData->maCtrlData.mnLastRadioWColor != rStyleSettings.GetWindowColor()) ||
2875 (pSVData->maCtrlData.mnLastRadioLColor != rStyleSettings.GetLightColor()) )
2877 pSVData->maCtrlData.maRadioImgList.clear();
2879 pSVData->maCtrlData.mnLastRadioFColor = rStyleSettings.GetFaceColor();
2880 pSVData->maCtrlData.mnLastRadioWColor = rStyleSettings.GetWindowColor();
2881 pSVData->maCtrlData.mnLastRadioLColor = rStyleSettings.GetLightColor();
2883 std::vector<OUString> aResources;
2884 if (nStyle)
2886 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO1);
2887 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO2);
2888 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO3);
2889 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO4);
2890 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO5);
2891 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO6);
2893 else
2895 aResources.emplace_back(SV_RESID_BITMAP_RADIO1);
2896 aResources.emplace_back(SV_RESID_BITMAP_RADIO2);
2897 aResources.emplace_back(SV_RESID_BITMAP_RADIO3);
2898 aResources.emplace_back(SV_RESID_BITMAP_RADIO4);
2899 aResources.emplace_back(SV_RESID_BITMAP_RADIO5);
2900 aResources.emplace_back(SV_RESID_BITMAP_RADIO6);
2902 LoadThemedImageList( rStyleSettings, pSVData->maCtrlData.maRadioImgList, aResources);
2903 pSVData->maCtrlData.mnRadioStyle = nStyle;
2906 sal_uInt16 nIndex;
2907 if ( nFlags & DrawButtonFlags::Disabled )
2909 if ( nFlags & DrawButtonFlags::Checked )
2910 nIndex = 5;
2911 else
2912 nIndex = 4;
2914 else if ( nFlags & DrawButtonFlags::Pressed )
2916 if ( nFlags & DrawButtonFlags::Checked )
2917 nIndex = 3;
2918 else
2919 nIndex = 2;
2921 else
2923 if ( nFlags & DrawButtonFlags::Checked )
2924 nIndex = 1;
2925 else
2926 nIndex = 0;
2928 return pSVData->maCtrlData.maRadioImgList[nIndex];
2931 void RadioButton::ImplAdjustNWFSizes()
2933 GetOutDev()->Push( vcl::PushFlags::MAPMODE );
2934 SetMapMode(MapMode(MapUnit::MapPixel));
2936 ImplControlValue aControlValue;
2937 Size aCurSize( GetSizePixel() );
2938 tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
2939 tools::Rectangle aBoundingRgn, aContentRgn;
2941 // get native size of a radiobutton
2942 if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
2943 ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
2944 aBoundingRgn, aContentRgn ) )
2946 Size aSize = aContentRgn.GetSize();
2948 if( aSize.Height() > aCurSize.Height() )
2950 aCurSize.setHeight( aSize.Height() );
2951 SetSizePixel( aCurSize );
2955 GetOutDev()->Pop();
2958 Size RadioButton::CalcMinimumSize(tools::Long nMaxWidth) const
2960 Size aSize;
2961 if ( !maImage )
2962 aSize = ImplGetRadioImageSize();
2963 else
2965 aSize = maImage.GetSizePixel();
2966 aSize.AdjustWidth(8);
2967 aSize.AdjustHeight(8);
2970 if (Button::HasImage())
2972 Size aImgSize = GetModeImage().GetSizePixel();
2973 aSize = Size(std::max(aImgSize.Width(), aSize.Width()),
2974 std::max(aImgSize.Height(), aSize.Height()));
2977 OUString aText = GetText();
2978 if (!aText.isEmpty())
2980 bool bTopImage = (GetStyle() & WB_TOP) != 0;
2982 Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
2983 aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
2985 aSize.AdjustWidth(2 ); // for focus rect
2987 if (!bTopImage)
2989 aSize.AdjustWidth(ImplGetImageToTextDistance() );
2990 aSize.AdjustWidth(aTextSize.Width() );
2991 if ( aSize.Height() < aTextSize.Height() )
2992 aSize.setHeight( aTextSize.Height() );
2994 else
2996 aSize.AdjustHeight(6 );
2997 aSize.AdjustHeight(GetTextHeight() );
2998 if ( aSize.Width() < aTextSize.Width() )
2999 aSize.setWidth( aTextSize.Width() );
3003 return CalcWindowSize( aSize );
3006 Size RadioButton::GetOptimalSize() const
3008 return CalcMinimumSize();
3011 void RadioButton::ShowFocus(const tools::Rectangle& rRect)
3013 if (IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Focus))
3015 ImplControlValue aControlValue;
3016 tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
3018 aInRect.SetLeft( rRect.Left() ); // exclude the radio element itself from the focusrect
3020 GetOutDev()->DrawNativeControl(ControlType::Radiobutton, ControlPart::Focus, aInRect,
3021 ControlState::FOCUSED, aControlValue, OUString());
3023 Button::ShowFocus(rRect);
3026 void RadioButton::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
3028 Button::DumpAsPropertyTree(rJsonWriter);
3029 rJsonWriter.put("checked", IsChecked());
3031 OUString sGroupId;
3032 std::vector<VclPtr<RadioButton>> aGroup = GetRadioButtonGroup();
3033 for(const auto& pButton : aGroup)
3034 sGroupId += pButton->get_id();
3036 if (!sGroupId.isEmpty())
3037 rJsonWriter.put("group", sGroupId);
3039 if (!!maImage)
3041 SvMemoryStream aOStm(6535, 6535);
3042 if(GraphicConverter::Export(aOStm, maImage.GetBitmapEx(), ConvertDataFormat::PNG) == ERRCODE_NONE)
3044 css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
3045 OStringBuffer aBuffer("data:image/png;base64,");
3046 ::comphelper::Base64::encode(aBuffer, aSeq);
3047 rJsonWriter.put("image", aBuffer);
3052 FactoryFunction RadioButton::GetUITestFactory() const
3054 return RadioButtonUIObject::create;
3057 void CheckBox::ImplInitCheckBoxData()
3059 meState = TRISTATE_FALSE;
3060 mbTriState = false;
3063 void CheckBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
3065 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
3066 Button::ImplInit( pParent, nStyle, nullptr );
3068 ImplInitSettings( true );
3071 WinBits CheckBox::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
3073 if ( !(nStyle & WB_NOTABSTOP) )
3074 nStyle |= WB_TABSTOP;
3075 if ( !(nStyle & WB_NOGROUP) &&
3076 (!pPrevWindow || (pPrevWindow->GetType() != WindowType::CHECKBOX)) )
3077 nStyle |= WB_GROUP;
3078 return nStyle;
3081 const vcl::Font& CheckBox::GetCanonicalFont( const StyleSettings& _rStyle ) const
3083 return _rStyle.GetRadioCheckFont();
3086 const Color& CheckBox::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
3088 return _rStyle.GetRadioCheckTextColor();
3091 void CheckBox::ImplInitSettings( bool bBackground )
3093 Button::ImplInitSettings();
3095 if ( !bBackground )
3096 return;
3098 vcl::Window* pParent = GetParent();
3099 if ( !IsControlBackground() &&
3100 (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) ) )
3102 EnableChildTransparentMode();
3103 SetParentClipMode( ParentClipMode::NoClip );
3104 SetPaintTransparent( true );
3105 SetBackground();
3106 if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
3107 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
3109 else
3111 EnableChildTransparentMode( false );
3112 SetParentClipMode();
3113 SetPaintTransparent( false );
3115 if ( IsControlBackground() )
3116 SetBackground( GetControlBackground() );
3117 else
3118 SetBackground( pParent->GetBackground() );
3122 void CheckBox::ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext)
3124 bool bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire);
3125 if (bNativeOK)
3127 ImplControlValue aControlValue(meState == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
3128 tools::Rectangle aCtrlRegion(maStateRect);
3129 ControlState nState = ControlState::NONE;
3131 if (HasFocus())
3132 nState |= ControlState::FOCUSED;
3133 if (GetButtonState() & DrawButtonFlags::Default)
3134 nState |= ControlState::DEFAULT;
3135 if (GetButtonState() & DrawButtonFlags::Pressed)
3136 nState |= ControlState::PRESSED;
3137 if (IsEnabled())
3138 nState |= ControlState::ENABLED;
3140 if (meState == TRISTATE_TRUE)
3141 aControlValue.setTristateVal(ButtonValue::On);
3142 else if (meState == TRISTATE_INDET)
3143 aControlValue.setTristateVal(ButtonValue::Mixed);
3145 if (IsMouseOver() && maMouseRect.Contains(GetPointerPosPixel()))
3146 nState |= ControlState::ROLLOVER;
3148 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3149 nState, aControlValue, OUString());
3152 if (bNativeOK)
3153 return;
3155 DrawButtonFlags nStyle = GetButtonState();
3156 if (!IsEnabled())
3157 nStyle |= DrawButtonFlags::Disabled;
3158 if (meState == TRISTATE_INDET)
3159 nStyle |= DrawButtonFlags::DontKnow;
3160 else if (meState == TRISTATE_TRUE)
3161 nStyle |= DrawButtonFlags::Checked;
3162 Image aImage = GetCheckImage(GetSettings(), nStyle);
3163 if (IsZoom())
3164 rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
3165 else
3166 rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
3169 void CheckBox::ImplDraw( OutputDevice* pDev, SystemTextColorFlags nSystemTextColorFlags,
3170 const Point& rPos, const Size& rSize,
3171 const Size& rImageSize, tools::Rectangle& rStateRect,
3172 tools::Rectangle& rMouseRect )
3174 WinBits nWinStyle = GetStyle();
3175 OUString aText( GetText() );
3177 pDev->Push( vcl::PushFlags::CLIPREGION | vcl::PushFlags::LINECOLOR );
3178 pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
3180 if (!aText.isEmpty() || HasImage())
3182 Button::ImplDrawRadioCheck(pDev, nWinStyle, nSystemTextColorFlags,
3183 rPos, rSize, rImageSize,
3184 rStateRect, rMouseRect);
3186 else
3188 rStateRect.SetLeft( rPos.X() );
3189 if ( nWinStyle & WB_VCENTER )
3190 rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
3191 else if ( nWinStyle & WB_BOTTOM )
3192 rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() );
3193 else
3194 rStateRect.SetTop( rPos.Y() );
3195 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
3196 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
3197 // provide space for focusrect
3198 // note: this assumes that the control's size was adjusted
3199 // accordingly in Get/LoseFocus, so the onscreen position won't change
3200 if( HasFocus() )
3201 rStateRect.Move( 1, 1 );
3202 rMouseRect = rStateRect;
3204 ImplSetFocusRect( rStateRect );
3207 pDev->Pop();
3210 void CheckBox::ImplDrawCheckBox(vcl::RenderContext& rRenderContext)
3212 Size aImageSize = ImplGetCheckImageSize();
3213 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
3214 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
3216 HideFocus();
3218 ImplDraw(&rRenderContext, SystemTextColorFlags::NONE, Point(), GetOutputSizePixel(),
3219 aImageSize, maStateRect, maMouseRect);
3221 ImplDrawCheckBoxState(rRenderContext);
3222 if (HasFocus())
3223 ShowFocus(ImplGetFocusRect());
3226 void CheckBox::ImplCheck()
3228 TriState eNewState;
3229 if ( meState == TRISTATE_FALSE )
3230 eNewState = TRISTATE_TRUE;
3231 else if ( !mbTriState )
3232 eNewState = TRISTATE_FALSE;
3233 else if ( meState == TRISTATE_TRUE )
3234 eNewState = TRISTATE_INDET;
3235 else
3236 eNewState = TRISTATE_FALSE;
3237 meState = eNewState;
3239 VclPtr<vcl::Window> xWindow = this;
3240 Invalidate();
3241 Toggle();
3242 if ( xWindow->isDisposed() )
3243 return;
3244 Click();
3247 CheckBox::CheckBox( vcl::Window* pParent, WinBits nStyle ) :
3248 Button( WindowType::CHECKBOX )
3250 ImplInitCheckBoxData();
3251 ImplInit( pParent, nStyle );
3254 void CheckBox::MouseButtonDown( const MouseEvent& rMEvt )
3256 if ( rMEvt.IsLeft() && maMouseRect.Contains( rMEvt.GetPosPixel() ) )
3258 GetButtonState() |= DrawButtonFlags::Pressed;
3259 Invalidate();
3260 StartTracking();
3261 return;
3264 Button::MouseButtonDown( rMEvt );
3267 void CheckBox::Tracking( const TrackingEvent& rTEvt )
3269 if ( rTEvt.IsTrackingEnded() )
3271 if ( GetButtonState() & DrawButtonFlags::Pressed )
3273 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
3274 GrabFocus();
3276 GetButtonState() &= ~DrawButtonFlags::Pressed;
3278 // do not call click handler if aborted
3279 if ( !rTEvt.IsTrackingCanceled() )
3280 ImplCheck();
3281 else
3283 Invalidate();
3287 else
3289 if ( maMouseRect.Contains( rTEvt.GetMouseEvent().GetPosPixel() ) )
3291 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
3293 GetButtonState() |= DrawButtonFlags::Pressed;
3294 Invalidate();
3297 else
3299 if ( GetButtonState() & DrawButtonFlags::Pressed )
3301 GetButtonState() &= ~DrawButtonFlags::Pressed;
3302 Invalidate();
3308 void CheckBox::KeyInput( const KeyEvent& rKEvt )
3310 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3312 if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
3314 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
3316 GetButtonState() |= DrawButtonFlags::Pressed;
3317 Invalidate();
3320 else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
3322 GetButtonState() &= ~DrawButtonFlags::Pressed;
3323 Invalidate();
3325 else
3326 Button::KeyInput( rKEvt );
3329 void CheckBox::KeyUp( const KeyEvent& rKEvt )
3331 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3333 if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
3335 GetButtonState() &= ~DrawButtonFlags::Pressed;
3336 ImplCheck();
3338 else
3339 Button::KeyUp( rKEvt );
3342 void CheckBox::FillLayoutData() const
3344 mxLayoutData.emplace();
3345 const_cast<CheckBox*>(this)->Invalidate();
3348 void CheckBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
3350 ImplDrawCheckBox(rRenderContext);
3353 void CheckBox::Draw( OutputDevice* pDev, const Point& rPos,
3354 SystemTextColorFlags nFlags )
3356 MapMode aResMapMode( MapUnit::Map100thMM );
3357 Point aPos = pDev->LogicToPixel( rPos );
3358 Size aSize = GetSizePixel();
3359 Size aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
3360 Size aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
3361 Size aBrd2Size = pDev->LogicToPixel( Size( 30, 30 ), aResMapMode );
3362 tools::Long nCheckWidth = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode ).Width();
3363 vcl::Font aFont = GetDrawPixelFont( pDev );
3364 tools::Rectangle aStateRect;
3365 tools::Rectangle aMouseRect;
3367 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
3368 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
3369 aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
3370 aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
3371 aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
3372 aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
3374 if ( !aBrd1Size.Width() )
3375 aBrd1Size.setWidth( 1 );
3376 if ( !aBrd1Size.Height() )
3377 aBrd1Size.setHeight( 1 );
3378 if ( !aBrd2Size.Width() )
3379 aBrd2Size.setWidth( 1 );
3380 if ( !aBrd2Size.Height() )
3381 aBrd2Size.setHeight( 1 );
3382 if ( !nCheckWidth )
3383 nCheckWidth = 1;
3385 pDev->Push();
3386 pDev->SetMapMode();
3387 pDev->SetFont( aFont );
3388 if ( nFlags & SystemTextColorFlags::Mono )
3389 pDev->SetTextColor( COL_BLACK );
3390 else
3391 pDev->SetTextColor( GetTextColor() );
3392 pDev->SetTextFillColor();
3394 ImplDraw( pDev, nFlags, aPos, aSize,
3395 aImageSize, aStateRect, aMouseRect );
3397 pDev->SetLineColor();
3398 pDev->SetFillColor( COL_BLACK );
3399 pDev->DrawRect( aStateRect );
3400 aStateRect.AdjustLeft(aBrd1Size.Width() );
3401 aStateRect.AdjustTop(aBrd1Size.Height() );
3402 aStateRect.AdjustRight( -(aBrd1Size.Width()) );
3403 aStateRect.AdjustBottom( -(aBrd1Size.Height()) );
3404 if ( meState == TRISTATE_INDET )
3405 pDev->SetFillColor( COL_LIGHTGRAY );
3406 else
3407 pDev->SetFillColor( COL_WHITE );
3408 pDev->DrawRect( aStateRect );
3410 if ( meState == TRISTATE_TRUE )
3412 aStateRect.AdjustLeft(aBrd2Size.Width() );
3413 aStateRect.AdjustTop(aBrd2Size.Height() );
3414 aStateRect.AdjustRight( -(aBrd2Size.Width()) );
3415 aStateRect.AdjustBottom( -(aBrd2Size.Height()) );
3416 Point aPos11( aStateRect.TopLeft() );
3417 Point aPos12( aStateRect.BottomRight() );
3418 Point aPos21( aStateRect.TopRight() );
3419 Point aPos22( aStateRect.BottomLeft() );
3420 Point aTempPos11( aPos11 );
3421 Point aTempPos12( aPos12 );
3422 Point aTempPos21( aPos21 );
3423 Point aTempPos22( aPos22 );
3424 pDev->SetLineColor( COL_BLACK );
3425 tools::Long nDX = 0;
3426 for ( tools::Long i = 0; i < nCheckWidth; i++ )
3428 if ( !(i % 2) )
3430 aTempPos11.setX( aPos11.X()+nDX );
3431 aTempPos12.setX( aPos12.X()+nDX );
3432 aTempPos21.setX( aPos21.X()+nDX );
3433 aTempPos22.setX( aPos22.X()+nDX );
3435 else
3437 nDX++;
3438 aTempPos11.setX( aPos11.X()-nDX );
3439 aTempPos12.setX( aPos12.X()-nDX );
3440 aTempPos21.setX( aPos21.X()-nDX );
3441 aTempPos22.setX( aPos22.X()-nDX );
3443 pDev->DrawLine( aTempPos11, aTempPos12 );
3444 pDev->DrawLine( aTempPos21, aTempPos22 );
3448 pDev->Pop();
3451 void CheckBox::Resize()
3453 Control::Resize();
3454 Invalidate();
3457 void CheckBox::GetFocus()
3459 if (GetText().isEmpty())
3461 // increase button size to have space for focus rect
3462 // checkboxes without text will draw focusrect around the check
3463 // See CheckBox::ImplDraw()
3464 Point aPos( GetPosPixel() );
3465 Size aSize( GetSizePixel() );
3466 aPos.Move(-1,-1);
3467 aSize.AdjustHeight(2 );
3468 aSize.AdjustWidth(2 );
3469 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
3470 Invalidate();
3471 // Trigger drawing to initialize the mouse rectangle, otherwise the mouse button down
3472 // handler would ignore the mouse event.
3473 PaintImmediately();
3475 else
3476 ShowFocus( ImplGetFocusRect() );
3478 SetInputContext( InputContext( GetFont() ) );
3479 Button::GetFocus();
3482 void CheckBox::LoseFocus()
3484 if ( GetButtonState() & DrawButtonFlags::Pressed )
3486 GetButtonState() &= ~DrawButtonFlags::Pressed;
3487 Invalidate();
3490 HideFocus();
3491 Button::LoseFocus();
3493 if (GetText().isEmpty())
3495 // decrease button size again (see GetFocus())
3496 // checkboxes without text will draw focusrect around the check
3497 Point aPos( GetPosPixel() );
3498 Size aSize( GetSizePixel() );
3499 aPos.Move(1,1);
3500 aSize.AdjustHeight( -2 );
3501 aSize.AdjustWidth( -2 );
3502 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
3503 Invalidate();
3507 void CheckBox::StateChanged( StateChangedType nType )
3509 Button::StateChanged( nType );
3511 if ( nType == StateChangedType::State )
3513 if ( IsReallyVisible() && IsUpdateMode() )
3514 Invalidate( maStateRect );
3516 else if ( (nType == StateChangedType::Enable) ||
3517 (nType == StateChangedType::Text) ||
3518 (nType == StateChangedType::Data) ||
3519 (nType == StateChangedType::UpdateMode) )
3521 if ( IsUpdateMode() )
3522 Invalidate();
3524 else if ( nType == StateChangedType::Style )
3526 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
3528 if ( (GetPrevStyle() & CHECKBOX_VIEW_STYLE) !=
3529 (GetStyle() & CHECKBOX_VIEW_STYLE) )
3531 if ( IsUpdateMode() )
3532 Invalidate();
3535 else if ( (nType == StateChangedType::Zoom) ||
3536 (nType == StateChangedType::ControlFont) )
3538 ImplInitSettings( false );
3539 Invalidate();
3541 else if ( nType == StateChangedType::ControlForeground )
3543 ImplInitSettings( false );
3544 Invalidate();
3546 else if ( nType == StateChangedType::ControlBackground )
3548 ImplInitSettings( true );
3549 Invalidate();
3553 void CheckBox::DataChanged( const DataChangedEvent& rDCEvt )
3555 Button::DataChanged( rDCEvt );
3557 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
3558 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
3559 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
3560 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
3562 ImplInitSettings( true );
3563 Invalidate();
3567 bool CheckBox::PreNotify( NotifyEvent& rNEvt )
3569 if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
3571 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
3572 if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
3574 // trigger redraw if mouse over state has changed
3575 if( IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire) )
3577 if (maMouseRect.Contains(GetPointerPosPixel()) != maMouseRect.Contains(GetLastPointerPosPixel()) ||
3578 pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
3580 Invalidate( maStateRect );
3586 return Button::PreNotify(rNEvt);
3589 void CheckBox::Toggle()
3591 ImplCallEventListenersAndHandler( VclEventId::CheckboxToggle, [this] () { maToggleHdl.Call(*this); } );
3594 void CheckBox::SetState( TriState eState )
3596 if ( !mbTriState && (eState == TRISTATE_INDET) )
3597 eState = TRISTATE_FALSE;
3599 if ( meState != eState )
3601 meState = eState;
3602 StateChanged( StateChangedType::State );
3603 Toggle();
3607 bool CheckBox::set_property(const OUString &rKey, const OUString &rValue)
3609 if (rKey == "active")
3610 SetState(toBool(rValue) ? TRISTATE_TRUE : TRISTATE_FALSE);
3611 else
3612 return Button::set_property(rKey, rValue);
3613 return true;
3616 void CheckBox::EnableTriState( bool bTriState )
3618 if ( mbTriState != bTriState )
3620 mbTriState = bTriState;
3622 if ( !bTriState && (meState == TRISTATE_INDET) )
3623 SetState( TRISTATE_FALSE );
3627 Size CheckBox::ImplGetCheckImageSize() const
3629 Size aSize;
3630 bool bDefaultSize = true;
3631 if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
3633 ImplControlValue aControlValue;
3634 tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
3635 tools::Rectangle aBoundingRgn, aContentRgn;
3637 // get native size of a check box
3638 if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3639 ControlState::DEFAULT|ControlState::ENABLED,
3640 aControlValue,
3641 aBoundingRgn, aContentRgn ) )
3643 aSize = aContentRgn.GetSize();
3644 bDefaultSize = false;
3647 if( bDefaultSize )
3648 aSize = GetCheckImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
3649 return aSize;
3652 Image CheckBox::GetCheckImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
3654 ImplSVData* pSVData = ImplGetSVData();
3655 const StyleSettings& rStyleSettings = rSettings.GetStyleSettings();
3656 sal_uInt16 nStyle = 0;
3658 if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
3659 nStyle = STYLE_CHECKBOX_MONO;
3661 if ( pSVData->maCtrlData.maCheckImgList.empty() ||
3662 (pSVData->maCtrlData.mnCheckStyle != nStyle) ||
3663 (pSVData->maCtrlData.mnLastCheckFColor != rStyleSettings.GetFaceColor()) ||
3664 (pSVData->maCtrlData.mnLastCheckWColor != rStyleSettings.GetWindowColor()) ||
3665 (pSVData->maCtrlData.mnLastCheckLColor != rStyleSettings.GetLightColor()) )
3667 pSVData->maCtrlData.maCheckImgList.clear();
3669 pSVData->maCtrlData.mnLastCheckFColor = rStyleSettings.GetFaceColor();
3670 pSVData->maCtrlData.mnLastCheckWColor = rStyleSettings.GetWindowColor();
3671 pSVData->maCtrlData.mnLastCheckLColor = rStyleSettings.GetLightColor();
3673 std::vector<OUString> aResources;
3674 if (nStyle)
3676 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO1);
3677 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO2);
3678 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO3);
3679 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO4);
3680 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO5);
3681 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO6);
3682 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO7);
3683 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO8);
3684 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO9);
3686 else
3688 aResources.emplace_back(SV_RESID_BITMAP_CHECK1);
3689 aResources.emplace_back(SV_RESID_BITMAP_CHECK2);
3690 aResources.emplace_back(SV_RESID_BITMAP_CHECK3);
3691 aResources.emplace_back(SV_RESID_BITMAP_CHECK4);
3692 aResources.emplace_back(SV_RESID_BITMAP_CHECK5);
3693 aResources.emplace_back(SV_RESID_BITMAP_CHECK6);
3694 aResources.emplace_back(SV_RESID_BITMAP_CHECK7);
3695 aResources.emplace_back(SV_RESID_BITMAP_CHECK8);
3696 aResources.emplace_back(SV_RESID_BITMAP_CHECK9);
3698 LoadThemedImageList(rStyleSettings, pSVData->maCtrlData.maCheckImgList, aResources);
3699 pSVData->maCtrlData.mnCheckStyle = nStyle;
3702 sal_uInt16 nIndex;
3703 if ( nFlags & DrawButtonFlags::Disabled )
3705 if ( nFlags & DrawButtonFlags::DontKnow )
3706 nIndex = 8;
3707 else if ( nFlags & DrawButtonFlags::Checked )
3708 nIndex = 5;
3709 else
3710 nIndex = 4;
3712 else if ( nFlags & DrawButtonFlags::Pressed )
3714 if ( nFlags & DrawButtonFlags::DontKnow )
3715 nIndex = 7;
3716 else if ( nFlags & DrawButtonFlags::Checked )
3717 nIndex = 3;
3718 else
3719 nIndex = 2;
3721 else
3723 if ( nFlags & DrawButtonFlags::DontKnow )
3724 nIndex = 6;
3725 else if ( nFlags & DrawButtonFlags::Checked )
3726 nIndex = 1;
3727 else
3728 nIndex = 0;
3730 return pSVData->maCtrlData.maCheckImgList[nIndex];
3733 void CheckBox::ImplAdjustNWFSizes()
3735 GetOutDev()->Push( vcl::PushFlags::MAPMODE );
3736 SetMapMode(MapMode(MapUnit::MapPixel));
3738 ImplControlValue aControlValue;
3739 Size aCurSize( GetSizePixel() );
3740 tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
3741 tools::Rectangle aBoundingRgn, aContentRgn;
3743 // get native size of a radiobutton
3744 if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3745 ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
3746 aBoundingRgn, aContentRgn ) )
3748 Size aSize = aContentRgn.GetSize();
3750 if( aSize.Height() > aCurSize.Height() )
3752 aCurSize.setHeight( aSize.Height() );
3753 SetSizePixel( aCurSize );
3757 GetOutDev()->Pop();
3760 Size CheckBox::CalcMinimumSize( tools::Long nMaxWidth ) const
3762 Size aSize = ImplGetCheckImageSize();
3763 nMaxWidth -= aSize.Width();
3765 OUString aText = GetText();
3766 if (!aText.isEmpty())
3768 // subtract what will be added later
3769 nMaxWidth-=2;
3770 nMaxWidth -= ImplGetImageToTextDistance();
3772 Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
3773 aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
3774 aSize.AdjustWidth(2 ); // for focus rect
3775 aSize.AdjustWidth(ImplGetImageToTextDistance() );
3776 aSize.AdjustWidth(aTextSize.Width() );
3777 if ( aSize.Height() < aTextSize.Height() )
3778 aSize.setHeight( aTextSize.Height() );
3780 else
3782 // is this still correct ? since the checkbox now
3783 // shows a focus rect it should be 2 pixels wider and longer
3784 /* since otherwise the controls in the Writer hang too far up
3785 aSize.Width() += 2;
3786 aSize.Height() += 2;
3790 return CalcWindowSize( aSize );
3793 Size CheckBox::GetOptimalSize() const
3795 int nWidthRequest(get_width_request());
3796 return CalcMinimumSize(nWidthRequest != -1 ? nWidthRequest : 0);
3799 void CheckBox::ShowFocus(const tools::Rectangle& rRect)
3801 if (IsNativeControlSupported(ControlType::Checkbox, ControlPart::Focus))
3803 ImplControlValue aControlValue;
3804 tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
3806 aInRect.SetLeft( rRect.Left() ); // exclude the checkbox itself from the focusrect
3808 GetOutDev()->DrawNativeControl(ControlType::Checkbox, ControlPart::Focus, aInRect,
3809 ControlState::FOCUSED, aControlValue, OUString());
3811 Button::ShowFocus(rRect);
3814 void CheckBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
3816 Button::DumpAsPropertyTree(rJsonWriter);
3817 rJsonWriter.put("checked", IsChecked());
3820 FactoryFunction CheckBox::GetUITestFactory() const
3822 return CheckBoxUIObject::create;
3825 ImageButton::ImageButton( vcl::Window* pParent, WinBits nStyle ) :
3826 PushButton( pParent, nStyle )
3828 ImplInitStyle();
3831 void ImageButton::ImplInitStyle()
3833 WinBits nStyle = GetStyle();
3835 if ( ! ( nStyle & ( WB_RIGHT | WB_LEFT ) ) )
3836 nStyle |= WB_CENTER;
3838 if ( ! ( nStyle & ( WB_TOP | WB_BOTTOM ) ) )
3839 nStyle |= WB_VCENTER;
3841 SetStyle( nStyle );
3844 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */