nss: upgrade to release 3.73
[LibreOffice.git] / vcl / source / control / button.cxx
blobd0614c4ab96cdd8e4aaec5ae5bf35adf13a46e79
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <tools/poly.hxx>
22 #include <vcl/image.hxx>
23 #include <vcl/bitmapex.hxx>
24 #include <vcl/decoview.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/toolkit/dialog.hxx>
29 #include <vcl/toolkit/fixed.hxx>
30 #include <vcl/toolkit/button.hxx>
31 #include <vcl/salnativewidgets.hxx>
32 #include <vcl/toolkit/edit.hxx>
33 #include <vcl/layout.hxx>
34 #include <vcl/stdtext.hxx>
35 #include <vcl/uitest/uiobject.hxx>
37 #include <bitmaps.hlst>
38 #include <svdata.hxx>
39 #include <window.h>
40 #include <controldata.hxx>
41 #include <vclstatuslistener.hxx>
42 #include <osl/diagnose.h>
44 #include <comphelper/dispatchcommand.hxx>
45 #include <comphelper/lok.hxx>
46 #include <officecfg/Office/Common.hxx>
47 #include <boost/property_tree/ptree.hpp>
48 #include <tools/json_writer.hxx>
51 using namespace css;
53 constexpr auto PUSHBUTTON_VIEW_STYLE = WB_3DLOOK |
54 WB_LEFT | WB_CENTER | WB_RIGHT |
55 WB_TOP | WB_VCENTER | WB_BOTTOM |
56 WB_WORDBREAK | WB_NOLABEL |
57 WB_DEFBUTTON | WB_NOLIGHTBORDER |
58 WB_RECTSTYLE | WB_SMALLSTYLE |
59 WB_TOGGLE;
60 constexpr auto RADIOBUTTON_VIEW_STYLE = WB_3DLOOK |
61 WB_LEFT | WB_CENTER | WB_RIGHT |
62 WB_TOP | WB_VCENTER | WB_BOTTOM |
63 WB_WORDBREAK | WB_NOLABEL;
64 constexpr auto CHECKBOX_VIEW_STYLE = WB_3DLOOK |
65 WB_LEFT | WB_CENTER | WB_RIGHT |
66 WB_TOP | WB_VCENTER | WB_BOTTOM |
67 WB_WORDBREAK | WB_NOLABEL;
69 #define STYLE_RADIOBUTTON_MONO (sal_uInt16(0x0001)) // legacy
70 #define STYLE_CHECKBOX_MONO (sal_uInt16(0x0001)) // legacy
72 class ImplCommonButtonData
74 public:
75 ImplCommonButtonData();
77 tools::Rectangle maFocusRect;
78 tools::Long mnSeparatorX;
79 DrawButtonFlags mnButtonState;
80 bool mbSmallSymbol;
82 Image maImage;
83 ImageAlign meImageAlign;
84 SymbolAlign meSymbolAlign;
86 /** StatusListener. Updates the button as the slot state changes */
87 rtl::Reference<VclStatusListener<Button>> mpStatusListener;
90 ImplCommonButtonData::ImplCommonButtonData() : maFocusRect(), mnSeparatorX(0), mnButtonState(DrawButtonFlags::NONE),
91 mbSmallSymbol(false), maImage(), meImageAlign(ImageAlign::Top), meSymbolAlign(SymbolAlign::LEFT)
95 Button::Button( WindowType nType ) :
96 Control( nType ),
97 mpButtonData( std::make_unique<ImplCommonButtonData>() )
101 Button::~Button()
103 disposeOnce();
106 void Button::dispose()
108 if (mpButtonData->mpStatusListener.is())
109 mpButtonData->mpStatusListener->dispose();
110 Control::dispose();
113 void Button::SetCommandHandler(const OUString& aCommand)
115 maCommand = aCommand;
116 SetClickHdl( LINK( this, Button, dispatchCommandHandler) );
118 mpButtonData->mpStatusListener = new VclStatusListener<Button>(this, aCommand);
119 mpButtonData->mpStatusListener->startListening();
122 void Button::Click()
124 ImplCallEventListenersAndHandler( VclEventId::ButtonClick, [this] () { maClickHdl.Call(this); } );
127 void Button::SetModeImage( const Image& rImage )
129 if ( rImage != mpButtonData->maImage )
131 mpButtonData->maImage = rImage;
132 StateChanged( StateChangedType::Data );
133 queue_resize();
137 Image const & Button::GetModeImage( ) const
139 return mpButtonData->maImage;
142 bool Button::HasImage() const
144 return !!(mpButtonData->maImage);
147 void Button::SetImageAlign( ImageAlign eAlign )
149 if ( mpButtonData->meImageAlign != eAlign )
151 mpButtonData->meImageAlign = eAlign;
152 StateChanged( StateChangedType::Data );
156 ImageAlign Button::GetImageAlign() const
158 return mpButtonData->meImageAlign;
161 tools::Long Button::ImplGetSeparatorX() const
163 return mpButtonData->mnSeparatorX;
166 void Button::ImplSetSeparatorX( tools::Long nX )
168 mpButtonData->mnSeparatorX = nX;
171 DrawTextFlags Button::ImplGetTextStyle( WinBits nWinStyle, DrawFlags nDrawFlags )
173 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
174 DrawTextFlags nTextStyle = FixedText::ImplGetTextStyle(nWinStyle & ~WB_DEFBUTTON);
176 if (!IsEnabled())
177 nTextStyle |= DrawTextFlags::Disable;
179 if ((nDrawFlags & DrawFlags::Mono) ||
180 (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
182 nTextStyle |= DrawTextFlags::Mono;
185 return nTextStyle;
188 void Button::ImplDrawAlignedImage(OutputDevice* pDev, Point& rPos,
189 Size& rSize,
190 sal_uLong nImageSep,
191 DrawTextFlags nTextStyle, tools::Rectangle *pSymbolRect,
192 bool bAddImageSep)
194 OUString aText(GetText());
195 bool bDrawImage = HasImage();
196 bool bDrawText = !aText.isEmpty();
197 bool bHasSymbol = pSymbolRect != nullptr;
199 // No text and no image => nothing to do => return
200 if (!bDrawImage && !bDrawText && !bHasSymbol)
201 return;
203 WinBits nWinStyle = GetStyle();
204 tools::Rectangle aOutRect( rPos, rSize );
205 ImageAlign eImageAlign = mpButtonData->meImageAlign;
206 Size aImageSize = mpButtonData->maImage.GetSizePixel();
208 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
209 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
211 // Drawing text or symbol only is simple, use style and output rectangle
212 if (bHasSymbol && !bDrawImage && !bDrawText)
214 *pSymbolRect = aOutRect;
215 return;
217 else if (bDrawText && !bDrawImage && !bHasSymbol)
219 aOutRect = DrawControlText(*pDev, aOutRect, aText, nTextStyle, nullptr, nullptr);
220 tools::Rectangle textRect = GetTextRect(
221 tools::Rectangle(Point(), Size(0x7fffffff, 0x7fffffff)), aText, nTextStyle);
222 // If the button text doesn't fit into it, put it into a tooltip (might happen in sidebar)
223 if (GetQuickHelpText().isEmpty() && textRect.getWidth() > rSize.getWidth())
224 SetQuickHelpText(aText);
226 ImplSetFocusRect(aOutRect);
227 rSize = aOutRect.GetSize();
228 rPos = aOutRect.TopLeft();
230 return;
233 // check for HC mode ( image only! )
234 Image* pImage = &(mpButtonData->maImage);
236 Size aTextSize;
237 Size aSymbolSize;
238 Size aDeviceTextSize;
239 Point aImagePos = rPos;
240 Point aTextPos = rPos;
241 tools::Rectangle aUnion(aImagePos, aImageSize);
242 tools::Long nSymbolHeight = 0;
244 if (bDrawText || bHasSymbol)
246 // Get the size of the text output area ( the symbol will be drawn in
247 // this area as well, so the symbol rectangle will be calculated here, too )
249 tools::Rectangle aRect(Point(), rSize);
250 Size aTSSize;
252 if (bHasSymbol)
254 tools::Rectangle aSymbol;
255 if (bDrawText)
257 nSymbolHeight = pDev->GetTextHeight();
258 if (mpButtonData->mbSmallSymbol)
259 nSymbolHeight = nSymbolHeight * 3 / 4;
261 aSymbol = tools::Rectangle(Point(), Size(nSymbolHeight, nSymbolHeight));
262 ImplCalcSymbolRect(aSymbol);
263 aRect.AdjustLeft(3 * nSymbolHeight / 2 );
264 aTSSize.setWidth( 3 * nSymbolHeight / 2 );
266 else
268 aSymbol = tools::Rectangle(Point(), rSize);
269 ImplCalcSymbolRect(aSymbol);
270 aTSSize.setWidth( aSymbol.GetWidth() );
272 aTSSize.setHeight( aSymbol.GetHeight() );
273 aSymbolSize = aSymbol.GetSize();
276 if (bDrawText)
278 if ((eImageAlign == ImageAlign::LeftTop) ||
279 (eImageAlign == ImageAlign::Left ) ||
280 (eImageAlign == ImageAlign::LeftBottom) ||
281 (eImageAlign == ImageAlign::RightTop) ||
282 (eImageAlign == ImageAlign::Right) ||
283 (eImageAlign == ImageAlign::RightBottom))
285 aRect.AdjustRight( -sal_Int32(aImageSize.Width() + nImageSep) );
287 else if ((eImageAlign == ImageAlign::TopLeft) ||
288 (eImageAlign == ImageAlign::Top) ||
289 (eImageAlign == ImageAlign::TopRight) ||
290 (eImageAlign == ImageAlign::BottomLeft) ||
291 (eImageAlign == ImageAlign::Bottom) ||
292 (eImageAlign == ImageAlign::BottomRight))
294 aRect.AdjustBottom( -sal_Int32(aImageSize.Height() + nImageSep) );
297 aRect = GetControlTextRect(*pDev, aRect, aText, nTextStyle, &aDeviceTextSize);
298 aTextSize = aRect.GetSize();
300 aTSSize.AdjustWidth(aTextSize.Width() );
302 if (aTSSize.Height() < aTextSize.Height())
303 aTSSize.setHeight( aTextSize.Height() );
305 if (bAddImageSep && bDrawImage)
307 tools::Long nDiff = (aImageSize.Height() - aTextSize.Height()) / 3;
308 if (nDiff > 0)
309 nImageSep += nDiff;
313 Size aMax;
314 aMax.setWidth( std::max(aTSSize.Width(), aImageSize.Width()) );
315 aMax.setHeight( std::max(aTSSize.Height(), aImageSize.Height()) );
317 // Now calculate the output area for the image and the text according to the image align flags
319 if ((eImageAlign == ImageAlign::Left) ||
320 (eImageAlign == ImageAlign::Right))
322 aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
323 aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
325 else if ((eImageAlign == ImageAlign::LeftBottom) ||
326 (eImageAlign == ImageAlign::RightBottom))
328 aImagePos.setY( rPos.Y() + aMax.Height() - aImageSize.Height() );
329 aTextPos.setY( rPos.Y() + aMax.Height() - aTSSize.Height() );
331 else if ((eImageAlign == ImageAlign::Top) ||
332 (eImageAlign == ImageAlign::Bottom))
334 aImagePos.setX( rPos.X() + (aMax.Width() - aImageSize.Width()) / 2 );
335 aTextPos.setX( rPos.X() + (aMax.Width() - aTSSize.Width()) / 2 );
337 else if ((eImageAlign == ImageAlign::TopRight) ||
338 (eImageAlign == ImageAlign::BottomRight))
340 aImagePos.setX( rPos.X() + aMax.Width() - aImageSize.Width() );
341 aTextPos.setX( rPos.X() + aMax.Width() - aTSSize.Width() );
344 if ((eImageAlign == ImageAlign::LeftTop) ||
345 (eImageAlign == ImageAlign::Left) ||
346 (eImageAlign == ImageAlign::LeftBottom))
348 aTextPos.setX( rPos.X() + aImageSize.Width() + nImageSep );
350 else if ((eImageAlign == ImageAlign::RightTop) ||
351 (eImageAlign == ImageAlign::Right) ||
352 (eImageAlign == ImageAlign::RightBottom))
354 aImagePos.setX( rPos.X() + aTSSize.Width() + nImageSep );
356 else if ((eImageAlign == ImageAlign::TopLeft) ||
357 (eImageAlign == ImageAlign::Top) ||
358 (eImageAlign == ImageAlign::TopRight))
360 aTextPos.setY( rPos.Y() + aImageSize.Height() + nImageSep );
362 else if ((eImageAlign == ImageAlign::BottomLeft) ||
363 (eImageAlign == ImageAlign::Bottom) ||
364 (eImageAlign == ImageAlign::BottomRight))
366 aImagePos.setY( rPos.Y() + aTSSize.Height() + nImageSep );
368 else if (eImageAlign == ImageAlign::Center)
370 aImagePos.setX( rPos.X() + (aMax.Width() - aImageSize.Width()) / 2 );
371 aImagePos.setY( rPos.Y() + (aMax.Height() - aImageSize.Height()) / 2 );
372 aTextPos.setX( rPos.X() + (aMax.Width() - aTSSize.Width()) / 2 );
373 aTextPos.setY( rPos.Y() + (aMax.Height() - aTSSize.Height()) / 2 );
375 aUnion = tools::Rectangle(aImagePos, aImageSize);
376 aUnion.Union(tools::Rectangle(aTextPos, aTSSize));
379 // Now place the combination of text and image in the output area of the button
380 // according to the window style (WinBits)
381 tools::Long nXOffset = 0;
382 tools::Long nYOffset = 0;
384 if (nWinStyle & WB_CENTER)
386 nXOffset = (rSize.Width() - aUnion.GetWidth()) / 2;
388 else if (nWinStyle & WB_RIGHT)
390 nXOffset = rSize.Width() - aUnion.GetWidth();
393 if (nWinStyle & WB_VCENTER)
395 nYOffset = (rSize.Height() - aUnion.GetHeight()) / 2;
397 else if (nWinStyle & WB_BOTTOM)
399 nYOffset = rSize.Height() - aUnion.GetHeight();
402 // the top left corner should always be visible, so we don't allow negative offsets
403 if (nXOffset < 0) nXOffset = 0;
404 if (nYOffset < 0) nYOffset = 0;
406 aImagePos.AdjustX(nXOffset );
407 aImagePos.AdjustY(nYOffset );
408 aTextPos.AdjustX(nXOffset );
409 aTextPos.AdjustY(nYOffset );
411 // set rPos and rSize to the union
412 rSize = aUnion.GetSize();
413 rPos.AdjustX(nXOffset );
414 rPos.AdjustY(nYOffset );
416 if (bHasSymbol)
418 if (mpButtonData->meSymbolAlign == SymbolAlign::RIGHT)
420 Point aRightPos(aTextPos.X() + aTextSize.Width() + aSymbolSize.Width() / 2, aTextPos.Y());
421 *pSymbolRect = tools::Rectangle(aRightPos, aSymbolSize);
423 else
425 *pSymbolRect = tools::Rectangle(aTextPos, aSymbolSize);
426 aTextPos.AdjustX(3 * nSymbolHeight / 2 );
428 if (mpButtonData->mbSmallSymbol)
430 nYOffset = (aUnion.GetHeight() - aSymbolSize.Height()) / 2;
431 pSymbolRect->setY(aTextPos.Y() + nYOffset);
435 DrawImageFlags nStyle = DrawImageFlags::NONE;
437 if (!IsEnabled())
439 nStyle |= DrawImageFlags::Disable;
442 if (IsZoom())
443 pDev->DrawImage(aImagePos, aImageSize, *pImage, nStyle);
444 else
445 pDev->DrawImage(aImagePos, *pImage, nStyle);
447 if (bDrawText)
449 const tools::Rectangle aTOutRect(aTextPos, aTextSize);
450 ImplSetFocusRect(aTOutRect);
451 DrawControlText(*pDev, aTOutRect, aText, nTextStyle, nullptr, nullptr, &aDeviceTextSize);
453 else
455 ImplSetFocusRect(tools::Rectangle(aImagePos, aImageSize));
459 void Button::ImplSetFocusRect(const tools::Rectangle &rFocusRect)
461 tools::Rectangle aFocusRect = rFocusRect;
462 tools::Rectangle aOutputRect(Point(), GetOutputSizePixel());
464 if (!aFocusRect.IsEmpty())
466 aFocusRect.AdjustLeft( -1 );
467 aFocusRect.AdjustTop( -1 );
468 aFocusRect.AdjustRight( 1 );
469 aFocusRect.AdjustBottom( 1 );
472 if (aFocusRect.Left() < aOutputRect.Left())
473 aFocusRect.SetLeft( aOutputRect.Left() );
474 if (aFocusRect.Top() < aOutputRect.Top())
475 aFocusRect.SetTop( aOutputRect.Top() );
476 if (aFocusRect.Right() > aOutputRect.Right())
477 aFocusRect.SetRight( aOutputRect.Right() );
478 if (aFocusRect.Bottom() > aOutputRect.Bottom())
479 aFocusRect.SetBottom( aOutputRect.Bottom() );
481 mpButtonData->maFocusRect = aFocusRect;
484 const tools::Rectangle& Button::ImplGetFocusRect() const
486 return mpButtonData->maFocusRect;
489 DrawButtonFlags& Button::GetButtonState()
491 return mpButtonData->mnButtonState;
494 DrawButtonFlags Button::GetButtonState() const
496 return mpButtonData->mnButtonState;
499 void Button::ImplSetSymbolAlign( SymbolAlign eAlign )
501 if ( mpButtonData->meSymbolAlign != eAlign )
503 mpButtonData->meSymbolAlign = eAlign;
504 StateChanged( StateChangedType::Data );
508 void Button::SetSmallSymbol()
510 mpButtonData->mbSmallSymbol = true;
513 bool Button::IsSmallSymbol () const
515 return mpButtonData->mbSmallSymbol;
518 bool Button::set_property(const OString &rKey, const OUString &rValue)
520 if (rKey == "image-position")
522 ImageAlign eAlign = ImageAlign::Left;
523 if (rValue == "left")
524 eAlign = ImageAlign::Left;
525 else if (rValue == "right")
526 eAlign = ImageAlign::Right;
527 else if (rValue == "top")
528 eAlign = ImageAlign::Top;
529 else if (rValue == "bottom")
530 eAlign = ImageAlign::Bottom;
531 SetImageAlign(eAlign);
533 else if (rKey == "focus-on-click")
535 WinBits nBits = GetStyle();
536 nBits &= ~WB_NOPOINTERFOCUS;
537 if (!toBool(rValue))
538 nBits |= WB_NOPOINTERFOCUS;
539 SetStyle(nBits);
541 else
542 return Control::set_property(rKey, rValue);
543 return true;
546 void Button::statusChanged(const css::frame::FeatureStateEvent& rEvent)
548 Enable(rEvent.IsEnabled);
551 FactoryFunction Button::GetUITestFactory() const
553 return ButtonUIObject::create;
556 void Button::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
558 Control::DumpAsPropertyTree(rJsonWriter);
559 rJsonWriter.put("text", GetText());
562 IMPL_STATIC_LINK( Button, dispatchCommandHandler, Button*, pButton, void )
564 if (pButton == nullptr)
565 return;
567 comphelper::dispatchCommand(pButton->maCommand, uno::Sequence<beans::PropertyValue>());
570 void PushButton::ImplInitPushButtonData()
572 mpWindowImpl->mbPushButton = true;
574 meSymbol = SymbolType::DONTKNOW;
575 meState = TRISTATE_FALSE;
576 mnDDStyle = PushButtonDropdownStyle::NONE;
577 mbIsActive = false;
578 mbPressed = false;
579 mbIsAction = false;
582 namespace
584 vcl::Window* getPreviousSibling(vcl::Window const *pParent)
586 return pParent ? pParent->GetWindow(GetWindowType::LastChild) : nullptr;
590 void PushButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
592 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
593 Button::ImplInit( pParent, nStyle, nullptr );
595 if ( nStyle & WB_NOLIGHTBORDER )
596 GetButtonState() |= DrawButtonFlags::NoLightBorder;
598 ImplInitSettings( true );
601 WinBits PushButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
603 if ( !(nStyle & WB_NOTABSTOP) )
604 nStyle |= WB_TABSTOP;
606 // if no alignment is given, default to "vertically centered". This is because since
607 // #i26046#, we respect the vertical alignment flags (previously we didn't completely),
608 // but we of course want to look as before when no vertical alignment is specified
609 if ( ( nStyle & ( WB_TOP | WB_VCENTER | WB_BOTTOM ) ) == 0 )
610 nStyle |= WB_VCENTER;
612 if ( !(nStyle & WB_NOGROUP) &&
613 (!pPrevWindow ||
614 ((pPrevWindow->GetType() != WindowType::PUSHBUTTON ) &&
615 (pPrevWindow->GetType() != WindowType::OKBUTTON ) &&
616 (pPrevWindow->GetType() != WindowType::CANCELBUTTON) &&
617 (pPrevWindow->GetType() != WindowType::HELPBUTTON )) ) )
618 nStyle |= WB_GROUP;
619 return nStyle;
622 const vcl::Font& PushButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
624 return _rStyle.GetPushButtonFont();
627 const Color& PushButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
629 return _rStyle.GetButtonTextColor();
632 void PushButton::ImplInitSettings( bool bBackground )
634 Button::ImplInitSettings();
636 if ( !bBackground )
637 return;
639 SetBackground();
640 // #i38498#: do not check for GetParent()->IsChildTransparentModeEnabled()
641 // otherwise the formcontrol button will be overdrawn due to ParentClipMode::NoClip
642 // for radio and checkbox this is ok as they should appear transparent in documents
643 if ( IsNativeControlSupported( ControlType::Pushbutton, ControlPart::Entire ) ||
644 (GetStyle() & WB_FLATBUTTON) != 0 )
646 EnableChildTransparentMode();
647 SetParentClipMode( ParentClipMode::NoClip );
648 SetPaintTransparent( true );
650 if ((GetStyle() & WB_FLATBUTTON) == 0)
651 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
652 else
653 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRectsForFlatButtons;
655 else
657 EnableChildTransparentMode( false );
658 SetParentClipMode();
659 SetPaintTransparent( false );
663 void PushButton::ImplDrawPushButtonFrame(vcl::RenderContext& rRenderContext,
664 tools::Rectangle& rRect, DrawButtonFlags nStyle)
666 if (!(GetStyle() & (WB_RECTSTYLE | WB_SMALLSTYLE)))
668 StyleSettings aStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
669 if (IsControlBackground())
670 aStyleSettings.Set3DColors(GetControlBackground());
673 DecorationView aDecoView(&rRenderContext);
674 if (IsControlBackground())
676 AllSettings aSettings = rRenderContext.GetSettings();
677 AllSettings aOldSettings = aSettings;
678 StyleSettings aStyleSettings = aSettings.GetStyleSettings();
679 aStyleSettings.Set3DColors(GetControlBackground());
680 aSettings.SetStyleSettings(aStyleSettings);
682 // Call OutputDevice::SetSettings() explicitly, as rRenderContext may
683 // be a vcl::Window in fact, and vcl::Window::SetSettings() will call
684 // Invalidate(), which is a problem, since we're in Paint().
685 rRenderContext.OutputDevice::SetSettings(aSettings);
686 rRect = aDecoView.DrawButton(rRect, nStyle);
687 rRenderContext.OutputDevice::SetSettings(aOldSettings);
689 else
690 rRect = aDecoView.DrawButton(rRect, nStyle);
693 bool PushButton::ImplHitTestPushButton( vcl::Window const * pDev,
694 const Point& rPos )
696 tools::Rectangle aTestRect( Point(), pDev->GetOutputSizePixel() );
698 return aTestRect.IsInside( rPos );
701 DrawTextFlags PushButton::ImplGetTextStyle( DrawFlags nDrawFlags ) const
703 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
705 DrawTextFlags nTextStyle = DrawTextFlags::Mnemonic | DrawTextFlags::MultiLine | DrawTextFlags::EndEllipsis;
707 if ( ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono ) ||
708 ( nDrawFlags & DrawFlags::Mono ) )
709 nTextStyle |= DrawTextFlags::Mono;
711 if ( GetStyle() & WB_WORDBREAK )
712 nTextStyle |= DrawTextFlags::WordBreak;
713 if ( GetStyle() & WB_NOLABEL )
714 nTextStyle &= ~DrawTextFlags::Mnemonic;
716 if ( GetStyle() & WB_LEFT )
717 nTextStyle |= DrawTextFlags::Left;
718 else if ( GetStyle() & WB_RIGHT )
719 nTextStyle |= DrawTextFlags::Right;
720 else
721 nTextStyle |= DrawTextFlags::Center;
723 if ( GetStyle() & WB_TOP )
724 nTextStyle |= DrawTextFlags::Top;
725 else if ( GetStyle() & WB_BOTTOM )
726 nTextStyle |= DrawTextFlags::Bottom;
727 else
728 nTextStyle |= DrawTextFlags::VCenter;
730 if ( !IsEnabled() )
731 nTextStyle |= DrawTextFlags::Disable;
733 return nTextStyle;
736 void PushButton::ImplDrawPushButtonContent(OutputDevice *pDev, DrawFlags nDrawFlags,
737 const tools::Rectangle &rRect, bool bMenuBtnSep,
738 DrawButtonFlags nButtonFlags)
740 const StyleSettings &rStyleSettings = GetSettings().GetStyleSettings();
741 tools::Rectangle aInRect = rRect;
742 Color aColor;
743 DrawTextFlags nTextStyle = ImplGetTextStyle(nDrawFlags);
744 DrawSymbolFlags nStyle;
746 if (aInRect.Right() < aInRect.Left() || aInRect.Bottom() < aInRect.Top())
747 return;
749 pDev->Push(PushFlags::CLIPREGION);
750 pDev->IntersectClipRegion(aInRect);
752 if (nDrawFlags & DrawFlags::Mono)
753 aColor = COL_BLACK;
755 // Custom foreground color is reasonable on stock controls only. Stock controls are used if a custom background has been set
756 // (and thus no native controls are able to be used) or no native controls are available.
758 else if (IsControlForeground()
759 && (IsControlBackground() || !IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire)))
760 aColor = GetControlForeground();
762 // Button types with possibly different text coloring are flat buttons and regular buttons. Regular buttons may be action
763 // buttons and may have an additional default status. Moreover all buttons may have an additional pressed and rollover
764 // (highlight) status. Pressed buttons are always in rollover status.
766 else if (GetStyle() & WB_FLATBUTTON)
767 if (nButtonFlags & DrawButtonFlags::Pressed)
768 aColor = rStyleSettings.GetFlatButtonPressedRolloverTextColor();
769 else if (nButtonFlags & DrawButtonFlags::Highlight)
770 aColor = rStyleSettings.GetFlatButtonRolloverTextColor();
771 else
772 aColor = rStyleSettings.GetFlatButtonTextColor();
773 else
774 if (isAction() && (nButtonFlags & DrawButtonFlags::Default))
775 if (nButtonFlags & DrawButtonFlags::Pressed)
776 aColor = rStyleSettings.GetDefaultActionButtonPressedRolloverTextColor();
777 else if (nButtonFlags & DrawButtonFlags::Highlight)
778 aColor = rStyleSettings.GetDefaultActionButtonRolloverTextColor();
779 else
780 aColor = rStyleSettings.GetDefaultActionButtonTextColor();
781 else if (isAction())
782 if (nButtonFlags & DrawButtonFlags::Pressed)
783 aColor = rStyleSettings.GetActionButtonPressedRolloverTextColor();
784 else if (nButtonFlags & DrawButtonFlags::Highlight)
785 aColor = rStyleSettings.GetActionButtonRolloverTextColor();
786 else
787 aColor = rStyleSettings.GetActionButtonTextColor();
788 else if (nButtonFlags & DrawButtonFlags::Default)
789 if (nButtonFlags & DrawButtonFlags::Pressed)
790 aColor = rStyleSettings.GetDefaultButtonPressedRolloverTextColor();
791 else if (nButtonFlags & DrawButtonFlags::Highlight)
792 aColor = rStyleSettings.GetDefaultButtonRolloverTextColor();
793 else
794 aColor = rStyleSettings.GetDefaultButtonTextColor();
795 else
796 if (nButtonFlags & DrawButtonFlags::Pressed)
797 aColor = rStyleSettings.GetButtonPressedRolloverTextColor();
798 else if (nButtonFlags & DrawButtonFlags::Highlight)
799 aColor = rStyleSettings.GetButtonRolloverTextColor();
800 else
801 aColor = rStyleSettings.GetButtonTextColor();
803 pDev->SetTextColor(aColor);
805 if ( IsEnabled() )
806 nStyle = DrawSymbolFlags::NONE;
807 else
808 nStyle = DrawSymbolFlags::Disable;
810 Size aSize = rRect.GetSize();
811 Point aPos = rRect.TopLeft();
813 sal_uLong nImageSep = 1 + (pDev->GetTextHeight()-10)/2;
814 if( nImageSep < 1 )
815 nImageSep = 1;
816 if ( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
817 mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
819 tools::Long nSeparatorX = 0;
820 tools::Rectangle aSymbolRect = aInRect;
822 // calculate symbol size
823 tools::Long nSymbolSize = pDev->GetTextHeight() / 2 + 1;
824 if (nSymbolSize > aSize.Width() / 2)
825 nSymbolSize = aSize.Width() / 2;
827 nSeparatorX = aInRect.Right() - 2*nSymbolSize;
829 // tdf#141761 Minimum width should be (1) Pixel, see comment
830 // with same task number above for more info
831 const tools::Long nWidthAdjust(2*nSymbolSize);
832 aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
834 // center symbol rectangle in the separated area
835 aSymbolRect.AdjustRight( -(nSymbolSize/2) );
836 aSymbolRect.SetLeft( aSymbolRect.Right() - nSymbolSize );
838 ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
839 nTextStyle, nullptr, true );
841 tools::Long nDistance = (aSymbolRect.GetHeight() > 10) ? 2 : 1;
842 DecorationView aDecoView( pDev );
843 if( bMenuBtnSep && nSeparatorX > 0 )
845 Point aStartPt( nSeparatorX, aSymbolRect.Top()+nDistance );
846 Point aEndPt( nSeparatorX, aSymbolRect.Bottom()-nDistance );
847 aDecoView.DrawSeparator( aStartPt, aEndPt );
849 ImplSetSeparatorX( nSeparatorX );
851 aDecoView.DrawSymbol( aSymbolRect, SymbolType::SPIN_DOWN, aColor, nStyle );
854 else
856 tools::Rectangle aSymbolRect;
857 ImplDrawAlignedImage( pDev, aPos, aSize, nImageSep,
858 nTextStyle, IsSymbol() ? &aSymbolRect : nullptr, true );
860 if ( IsSymbol() )
862 DecorationView aDecoView( pDev );
863 aDecoView.DrawSymbol( aSymbolRect, meSymbol, aColor, nStyle );
867 pDev->Pop(); // restore clipregion
870 void PushButton::ImplDrawPushButton(vcl::RenderContext& rRenderContext)
872 HideFocus();
874 DrawButtonFlags nButtonStyle = GetButtonState();
875 Size aOutSz(GetOutputSizePixel());
876 tools::Rectangle aRect(Point(), aOutSz);
877 tools::Rectangle aInRect = aRect;
878 bool bNativeOK = false;
880 // adjust style if button should be rendered 'pressed'
881 if (mbPressed || mbIsActive)
882 nButtonStyle |= DrawButtonFlags::Pressed;
884 // TODO: move this to Window class or make it a member !!!
885 ControlType aCtrlType = ControlType::Generic;
886 switch(GetParent()->GetType())
888 case WindowType::LISTBOX:
889 case WindowType::MULTILISTBOX:
890 case WindowType::TREELISTBOX:
891 aCtrlType = ControlType::Listbox;
892 break;
894 case WindowType::COMBOBOX:
895 case WindowType::PATTERNBOX:
896 case WindowType::NUMERICBOX:
897 case WindowType::METRICBOX:
898 case WindowType::CURRENCYBOX:
899 case WindowType::DATEBOX:
900 case WindowType::TIMEBOX:
901 case WindowType::LONGCURRENCYBOX:
902 aCtrlType = ControlType::Combobox;
903 break;
904 default:
905 break;
908 bool bDropDown = (IsSymbol() && (GetSymbol() == SymbolType::SPIN_DOWN) && GetText().isEmpty());
910 if( bDropDown && (aCtrlType == ControlType::Combobox || aCtrlType == ControlType::Listbox))
912 if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::Entire))
914 // skip painting if the button was already drawn by the theme
915 if (aCtrlType == ControlType::Combobox)
917 Edit* pEdit = static_cast<Edit*>(GetParent());
918 if (pEdit->ImplUseNativeBorder(rRenderContext, pEdit->GetStyle()))
919 bNativeOK = true;
921 else if (GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::HasBackgroundTexture))
923 bNativeOK = true;
926 if (!bNativeOK && GetParent()->IsNativeControlSupported(aCtrlType, ControlPart::ButtonDown))
928 // let the theme draw it, note we then need support
929 // for ControlType::Listbox/ControlPart::ButtonDown and ControlType::Combobox/ControlPart::ButtonDown
931 ImplControlValue aControlValue;
932 ControlState nState = ControlState::NONE;
934 if (mbPressed || mbIsActive)
935 nState |= ControlState::PRESSED;
936 if (GetButtonState() & DrawButtonFlags::Pressed)
937 nState |= ControlState::PRESSED;
938 if (HasFocus())
939 nState |= ControlState::FOCUSED;
940 if (GetButtonState() & DrawButtonFlags::Default)
941 nState |= ControlState::DEFAULT;
942 if (Window::IsEnabled())
943 nState |= ControlState::ENABLED;
945 if (IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()))
946 nState |= ControlState::ROLLOVER;
948 if ( IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()) && mbIsActive)
950 nState |= ControlState::ROLLOVER;
951 nButtonStyle &= ~DrawButtonFlags::Pressed;
954 bNativeOK = rRenderContext.DrawNativeControl(aCtrlType, ControlPart::ButtonDown, aInRect, nState,
955 aControlValue, OUString());
960 if (bNativeOK)
961 return;
963 bool bRollOver = (IsMouseOver() && aInRect.IsInside(GetPointerPosPixel()));
964 if (bRollOver)
965 nButtonStyle |= DrawButtonFlags::Highlight;
966 bool bDrawMenuSep = mnDDStyle == PushButtonDropdownStyle::SplitMenuButton;
967 if (GetStyle() & WB_FLATBUTTON)
969 if (!bRollOver && !HasFocus())
970 bDrawMenuSep = false;
972 // tdf#123175 if there is a custom control bg set, draw the button without outsourcing to the NWF
973 bNativeOK = !IsControlBackground() && rRenderContext.IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire);
974 if (bNativeOK)
976 PushButtonValue aControlValue;
977 aControlValue.mbIsAction = isAction();
979 tools::Rectangle aCtrlRegion(aInRect);
980 ControlState nState = ControlState::NONE;
982 if (mbPressed || IsChecked() || mbIsActive)
984 nState |= ControlState::PRESSED;
985 nButtonStyle |= DrawButtonFlags::Pressed;
987 if (GetButtonState() & DrawButtonFlags::Pressed)
988 nState |= ControlState::PRESSED;
989 if (HasFocus())
990 nState |= ControlState::FOCUSED;
991 if (GetButtonState() & DrawButtonFlags::Default)
992 nState |= ControlState::DEFAULT;
993 if (Window::IsEnabled())
994 nState |= ControlState::ENABLED;
996 if (bRollOver || mbIsActive)
998 nButtonStyle |= DrawButtonFlags::Highlight;
999 nState |= ControlState::ROLLOVER;
1002 if (mbIsActive && bRollOver)
1004 nState &= ~ControlState::PRESSED;
1005 nButtonStyle &= ~DrawButtonFlags::Pressed;
1008 if (GetStyle() & WB_FLATBUTTON)
1009 aControlValue.m_bFlatButton = true;
1010 if (GetStyle() & WB_BEVELBUTTON)
1011 aControlValue.mbBevelButton = true;
1013 // draw frame into invisible window to have aInRect modified correctly
1014 // but do not shift the inner rect for pressed buttons (ie remove DrawButtonFlags::Pressed)
1015 // this assumes the theme has enough visual cues to signalize the button was pressed
1016 //Window aWin( this );
1017 //ImplDrawPushButtonFrame( &aWin, aInRect, nButtonStyle & ~DrawButtonFlags::Pressed );
1019 // looks better this way as symbols were displaced slightly using the above approach
1020 aInRect.AdjustTop(4 );
1021 aInRect.AdjustBottom( -4 );
1022 aInRect.AdjustLeft(4 );
1023 aInRect.AdjustRight( -4 );
1025 // prepare single line hint (needed on mac to decide between normal push button and
1026 // rectangular bevel button look)
1027 Size aFontSize(Application::GetSettings().GetStyleSettings().GetPushButtonFont().GetFontSize());
1028 aFontSize = rRenderContext.LogicToPixel(aFontSize, MapMode(MapUnit::MapPoint));
1029 Size aInRectSize(rRenderContext.LogicToPixel(Size(aInRect.GetWidth(), aInRect.GetHeight())));
1030 aControlValue.mbSingleLine = (aInRectSize.Height() < 2 * aFontSize.Height());
1032 if ((nState & ControlState::ROLLOVER) || !(GetStyle() & WB_FLATBUTTON)
1033 || (HasFocus() && mpWindowImpl->mbUseNativeFocus
1034 && !IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus)))
1036 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion, nState,
1037 aControlValue, OUString() /*PushButton::GetText()*/);
1039 else
1041 bNativeOK = true;
1044 // draw content using the same aInRect as non-native VCL would do
1045 ImplDrawPushButtonContent(&rRenderContext, DrawFlags::NONE,
1046 aInRect, bDrawMenuSep, nButtonStyle);
1048 if (HasFocus())
1049 ShowFocus(ImplGetFocusRect());
1052 if (bNativeOK)
1053 return;
1055 // draw PushButtonFrame, aInRect has content size afterwards
1056 if (GetStyle() & WB_FLATBUTTON)
1058 tools::Rectangle aTempRect(aInRect);
1059 if (bRollOver)
1060 ImplDrawPushButtonFrame(rRenderContext, aTempRect, nButtonStyle);
1061 aInRect.AdjustLeft(2 );
1062 aInRect.AdjustTop(2 );
1063 aInRect.AdjustRight( -2 );
1064 aInRect.AdjustBottom( -2 );
1066 else
1068 ImplDrawPushButtonFrame(rRenderContext, aInRect, nButtonStyle);
1071 // draw content
1072 ImplDrawPushButtonContent(&rRenderContext, DrawFlags::NONE, aInRect, bDrawMenuSep, nButtonStyle);
1074 if (HasFocus())
1076 ShowFocus(ImplGetFocusRect());
1080 void PushButton::ImplSetDefButton( bool bSet )
1082 Size aSize( GetSizePixel() );
1083 Point aPos( GetPosPixel() );
1084 int dLeft(0), dRight(0), dTop(0), dBottom(0);
1085 bool bSetPos = false;
1087 if ( IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
1089 tools::Rectangle aBound, aCont;
1090 tools::Rectangle aCtrlRegion( 0, 0, 80, 20 ); // use a constant size to avoid accumulating
1091 // will not work if the theme has dynamic adornment sizes
1092 ImplControlValue aControlValue;
1094 // get native size of a 'default' button
1095 // and adjust the VCL button if more space for adornment is required
1096 if( GetNativeControlRegion( ControlType::Pushbutton, ControlPart::Entire, aCtrlRegion,
1097 ControlState::DEFAULT|ControlState::ENABLED,
1098 aControlValue,
1099 aBound, aCont ) )
1101 dLeft = aCont.Left() - aBound.Left();
1102 dTop = aCont.Top() - aBound.Top();
1103 dRight = aBound.Right() - aCont.Right();
1104 dBottom = aBound.Bottom() - aCont.Bottom();
1105 bSetPos = dLeft || dTop || dRight || dBottom;
1109 if ( bSet )
1111 if( !(GetButtonState() & DrawButtonFlags::Default) && bSetPos )
1113 // adjust pos/size when toggling from non-default to default
1114 aPos.Move(-dLeft, -dTop);
1115 aSize.AdjustWidth(dLeft + dRight );
1116 aSize.AdjustHeight(dTop + dBottom );
1118 GetButtonState() |= DrawButtonFlags::Default;
1120 else
1122 if( (GetButtonState() & DrawButtonFlags::Default) && bSetPos )
1124 // adjust pos/size when toggling from default to non-default
1125 aPos.Move(dLeft, dTop);
1126 aSize.AdjustWidth( -(dLeft + dRight) );
1127 aSize.AdjustHeight( -(dTop + dBottom) );
1129 GetButtonState() &= ~DrawButtonFlags::Default;
1131 if( bSetPos )
1132 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
1134 Invalidate();
1137 bool PushButton::ImplIsDefButton() const
1139 return bool(GetButtonState() & DrawButtonFlags::Default);
1142 PushButton::PushButton( WindowType nType ) :
1143 Button( nType )
1145 ImplInitPushButtonData();
1148 PushButton::PushButton( vcl::Window* pParent, WinBits nStyle ) :
1149 Button( WindowType::PUSHBUTTON )
1151 ImplInitPushButtonData();
1152 ImplInit( pParent, nStyle );
1155 void PushButton::MouseButtonDown( const MouseEvent& rMEvt )
1157 if ( !(rMEvt.IsLeft() &&
1158 ImplHitTestPushButton( this, rMEvt.GetPosPixel() )) )
1159 return;
1161 StartTrackingFlags nTrackFlags = StartTrackingFlags::NONE;
1163 if ( ( GetStyle() & WB_REPEAT ) &&
1164 ! ( GetStyle() & WB_TOGGLE ) )
1165 nTrackFlags |= StartTrackingFlags::ButtonRepeat;
1167 GetButtonState() |= DrawButtonFlags::Pressed;
1168 Invalidate();
1169 StartTracking( nTrackFlags );
1171 if ( nTrackFlags & StartTrackingFlags::ButtonRepeat )
1172 Click();
1175 void PushButton::Tracking( const TrackingEvent& rTEvt )
1177 if ( rTEvt.IsTrackingEnded() )
1179 if ( GetButtonState() & DrawButtonFlags::Pressed )
1181 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
1182 GrabFocus();
1184 if ( GetStyle() & WB_TOGGLE )
1186 // Don't toggle, when aborted
1187 if ( !rTEvt.IsTrackingCanceled() )
1189 if ( IsChecked() )
1191 Check( false );
1192 GetButtonState() &= ~DrawButtonFlags::Pressed;
1194 else
1195 Check();
1198 else
1199 GetButtonState() &= ~DrawButtonFlags::Pressed;
1201 Invalidate();
1203 // do not call Click handler if aborted
1204 if ( !rTEvt.IsTrackingCanceled() )
1206 if ( ! ( GetStyle() & WB_REPEAT ) || ( GetStyle() & WB_TOGGLE ) )
1207 Click();
1211 else
1213 if ( ImplHitTestPushButton( this, rTEvt.GetMouseEvent().GetPosPixel() ) )
1215 if ( GetButtonState() & DrawButtonFlags::Pressed )
1217 if ( rTEvt.IsTrackingRepeat() && (GetStyle() & WB_REPEAT) &&
1218 ! ( GetStyle() & WB_TOGGLE ) )
1219 Click();
1221 else
1223 GetButtonState() |= DrawButtonFlags::Pressed;
1224 Invalidate();
1227 else
1229 if ( GetButtonState() & DrawButtonFlags::Pressed )
1231 GetButtonState() &= ~DrawButtonFlags::Pressed;
1232 Invalidate();
1238 void PushButton::KeyInput( const KeyEvent& rKEvt )
1240 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1242 if ( !aKeyCode.GetModifier() &&
1243 ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
1245 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
1247 GetButtonState() |= DrawButtonFlags::Pressed;
1248 Invalidate();
1251 if ( ( GetStyle() & WB_REPEAT ) &&
1252 ! ( GetStyle() & WB_TOGGLE ) )
1253 Click();
1255 else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
1257 GetButtonState() &= ~DrawButtonFlags::Pressed;
1258 Invalidate();
1260 else
1261 Button::KeyInput( rKEvt );
1264 void PushButton::KeyUp( const KeyEvent& rKEvt )
1266 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1268 if ( (GetButtonState() & DrawButtonFlags::Pressed) &&
1269 ((aKeyCode.GetCode() == KEY_RETURN) || (aKeyCode.GetCode() == KEY_SPACE)) )
1271 if ( GetStyle() & WB_TOGGLE )
1273 if ( IsChecked() )
1275 Check( false );
1276 GetButtonState() &= ~DrawButtonFlags::Pressed;
1278 else
1279 Check();
1281 Toggle();
1283 else
1284 GetButtonState() &= ~DrawButtonFlags::Pressed;
1286 Invalidate();
1288 if ( !( GetStyle() & WB_REPEAT ) || ( GetStyle() & WB_TOGGLE ) )
1289 Click();
1291 else
1292 Button::KeyUp( rKEvt );
1295 void PushButton::FillLayoutData() const
1297 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
1298 const_cast<PushButton*>(this)->Invalidate();
1301 void PushButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1303 ImplDrawPushButton(rRenderContext);
1306 void PushButton::Draw( OutputDevice* pDev, const Point& rPos,
1307 DrawFlags nFlags )
1309 Point aPos = pDev->LogicToPixel( rPos );
1310 Size aSize = GetSizePixel();
1311 tools::Rectangle aRect( aPos, aSize );
1312 vcl::Font aFont = GetDrawPixelFont( pDev );
1314 pDev->Push();
1315 pDev->SetMapMode();
1316 pDev->SetFont( aFont );
1317 if ( nFlags & DrawFlags::Mono )
1319 pDev->SetTextColor( COL_BLACK );
1321 else
1323 pDev->SetTextColor( GetTextColor() );
1325 // DecoView uses the FaceColor...
1326 AllSettings aSettings = pDev->GetSettings();
1327 StyleSettings aStyleSettings = aSettings.GetStyleSettings();
1328 if ( IsControlBackground() )
1329 aStyleSettings.SetFaceColor( GetControlBackground() );
1330 else
1331 aStyleSettings.SetFaceColor( GetSettings().GetStyleSettings().GetFaceColor() );
1332 aSettings.SetStyleSettings( aStyleSettings );
1333 pDev->OutputDevice::SetSettings( aSettings );
1335 pDev->SetTextFillColor();
1337 DecorationView aDecoView( pDev );
1338 DrawButtonFlags nButtonStyle = DrawButtonFlags::NONE;
1339 if ( nFlags & DrawFlags::Mono )
1340 nButtonStyle |= DrawButtonFlags::Mono;
1341 if ( IsChecked() )
1342 nButtonStyle |= DrawButtonFlags::Checked;
1343 aRect = aDecoView.DrawButton( aRect, nButtonStyle );
1345 ImplDrawPushButtonContent( pDev, nFlags, aRect, true, nButtonStyle );
1346 pDev->Pop();
1349 void PushButton::Resize()
1351 Control::Resize();
1352 Invalidate();
1355 void PushButton::GetFocus()
1357 ShowFocus( ImplGetFocusRect() );
1358 SetInputContext( InputContext( GetFont() ) );
1359 Button::GetFocus();
1362 void PushButton::LoseFocus()
1364 EndSelection();
1365 HideFocus();
1366 Button::LoseFocus();
1369 void PushButton::StateChanged( StateChangedType nType )
1371 Button::StateChanged( nType );
1373 if ( (nType == StateChangedType::Enable) ||
1374 (nType == StateChangedType::Text) ||
1375 (nType == StateChangedType::Data) ||
1376 (nType == StateChangedType::State) ||
1377 (nType == StateChangedType::UpdateMode) )
1379 if ( IsReallyVisible() && IsUpdateMode() )
1380 Invalidate();
1382 else if ( nType == StateChangedType::Style )
1384 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
1386 bool bIsDefButton = ( GetStyle() & WB_DEFBUTTON ) != 0;
1387 bool bWasDefButton = ( GetPrevStyle() & WB_DEFBUTTON ) != 0;
1388 if ( bIsDefButton != bWasDefButton )
1389 ImplSetDefButton( bIsDefButton );
1391 if ( IsReallyVisible() && IsUpdateMode() )
1393 if ( (GetPrevStyle() & PUSHBUTTON_VIEW_STYLE) !=
1394 (GetStyle() & PUSHBUTTON_VIEW_STYLE) )
1395 Invalidate();
1398 else if ( (nType == StateChangedType::Zoom) ||
1399 (nType == StateChangedType::ControlFont) )
1401 ImplInitSettings( false );
1402 Invalidate();
1404 else if ( nType == StateChangedType::ControlForeground )
1406 ImplInitSettings( false );
1407 Invalidate();
1409 else if ( nType == StateChangedType::ControlBackground )
1411 ImplInitSettings( true );
1412 Invalidate();
1416 void PushButton::DataChanged( const DataChangedEvent& rDCEvt )
1418 Button::DataChanged( rDCEvt );
1420 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1421 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1422 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1423 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1425 ImplInitSettings( true );
1426 Invalidate();
1430 bool PushButton::PreNotify( NotifyEvent& rNEvt )
1432 if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
1434 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
1435 if( pMouseEvt && (pMouseEvt->IsEnterWindow() || pMouseEvt->IsLeaveWindow()) )
1437 // trigger redraw as mouse over state has changed
1439 // TODO: move this to Window class or make it a member !!!
1440 ControlType aCtrlType = ControlType::Generic;
1441 switch( GetParent()->GetType() )
1443 case WindowType::LISTBOX:
1444 case WindowType::MULTILISTBOX:
1445 case WindowType::TREELISTBOX:
1446 aCtrlType = ControlType::Listbox;
1447 break;
1449 case WindowType::COMBOBOX:
1450 case WindowType::PATTERNBOX:
1451 case WindowType::NUMERICBOX:
1452 case WindowType::METRICBOX:
1453 case WindowType::CURRENCYBOX:
1454 case WindowType::DATEBOX:
1455 case WindowType::TIMEBOX:
1456 case WindowType::LONGCURRENCYBOX:
1457 aCtrlType = ControlType::Combobox;
1458 break;
1459 default:
1460 break;
1463 bool bDropDown = ( IsSymbol() && (GetSymbol()==SymbolType::SPIN_DOWN) && GetText().isEmpty() );
1465 if( bDropDown && GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::Entire) &&
1466 !GetParent()->IsNativeControlSupported( aCtrlType, ControlPart::ButtonDown) )
1468 vcl::Window *pBorder = GetParent()->GetWindow( GetWindowType::Border );
1469 if(aCtrlType == ControlType::Combobox)
1471 // only paint the button part to avoid flickering of the combobox text
1472 tools::Rectangle aClipRect( Point(), GetOutputSizePixel() );
1473 aClipRect.SetPos(pBorder->ScreenToOutputPixel(OutputToScreenPixel(aClipRect.TopLeft())));
1474 pBorder->Invalidate( aClipRect );
1476 else
1478 pBorder->Invalidate( InvalidateFlags::NoErase );
1481 else if( (GetStyle() & WB_FLATBUTTON) ||
1482 IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Entire) )
1484 Invalidate();
1489 return Button::PreNotify(rNEvt);
1492 void PushButton::Toggle()
1494 ImplCallEventListenersAndHandler( VclEventId::PushbuttonToggle, nullptr );
1497 void PushButton::SetSymbol( SymbolType eSymbol )
1499 if ( meSymbol != eSymbol )
1501 meSymbol = eSymbol;
1502 CompatStateChanged( StateChangedType::Data );
1506 void PushButton::SetSymbolAlign( SymbolAlign eAlign )
1508 ImplSetSymbolAlign( eAlign );
1511 void PushButton::SetDropDown( PushButtonDropdownStyle nStyle )
1513 if ( mnDDStyle != nStyle )
1515 mnDDStyle = nStyle;
1516 CompatStateChanged( StateChangedType::Data );
1520 void PushButton::SetState( TriState eState )
1522 if ( meState == eState )
1523 return;
1525 meState = eState;
1526 if ( meState == TRISTATE_FALSE )
1527 GetButtonState() &= ~DrawButtonFlags(DrawButtonFlags::Checked | DrawButtonFlags::DontKnow);
1528 else if ( meState == TRISTATE_TRUE )
1530 GetButtonState() &= ~DrawButtonFlags::DontKnow;
1531 GetButtonState() |= DrawButtonFlags::Checked;
1533 else // TRISTATE_INDET
1535 GetButtonState() &= ~DrawButtonFlags::Checked;
1536 GetButtonState() |= DrawButtonFlags::DontKnow;
1539 CompatStateChanged( StateChangedType::State );
1540 Toggle();
1543 void PushButton::statusChanged(const css::frame::FeatureStateEvent& rEvent)
1545 Button::statusChanged(rEvent);
1546 if (rEvent.State.has<bool>())
1547 SetPressed(rEvent.State.get<bool>());
1550 void PushButton::SetPressed( bool bPressed )
1552 if ( mbPressed != bPressed )
1554 mbPressed = bPressed;
1555 CompatStateChanged( StateChangedType::Data );
1559 void PushButton::EndSelection()
1561 EndTracking( TrackingEventFlags::Cancel );
1562 if ( !IsDisposed() &&
1563 GetButtonState() & DrawButtonFlags::Pressed )
1565 GetButtonState() &= ~DrawButtonFlags::Pressed;
1566 if ( !mbPressed )
1567 Invalidate();
1571 Size PushButton::CalcMinimumSize() const
1573 Size aSize;
1575 if ( IsSymbol() )
1577 if ( IsSmallSymbol ())
1578 aSize = Size( 16, 12 );
1579 else
1580 aSize = Size( 26, 24 );
1582 else if ( Button::HasImage() )
1583 aSize = GetModeImage().GetSizePixel();
1584 if( mnDDStyle == PushButtonDropdownStyle::MenuButton ||
1585 mnDDStyle == PushButtonDropdownStyle::SplitMenuButton )
1587 tools::Long nSymbolSize = GetTextHeight() / 2 + 1;
1588 aSize.AdjustWidth(2*nSymbolSize );
1590 if (!PushButton::GetText().isEmpty())
1592 Size textSize = GetTextRect( tools::Rectangle( Point(), Size( 0x7fffffff, 0x7fffffff ) ),
1593 PushButton::GetText(), ImplGetTextStyle( DrawFlags::NONE ) ).GetSize();
1594 aSize.AdjustWidth(textSize.Width() );
1595 aSize.setHeight( std::max( aSize.Height(), tools::Long( textSize.Height() * 1.15 ) ) );
1598 // cf. ImplDrawPushButton ...
1599 if( (GetStyle() & WB_SMALLSTYLE) == 0 )
1601 aSize.AdjustWidth(24 );
1602 aSize.AdjustHeight(12 );
1605 return CalcWindowSize( aSize );
1608 Size PushButton::GetOptimalSize() const
1610 return CalcMinimumSize();
1613 bool PushButton::set_property(const OString &rKey, const OUString &rValue)
1615 if (rKey == "has-default")
1617 WinBits nBits = GetStyle();
1618 nBits &= ~WB_DEFBUTTON;
1619 if (toBool(rValue))
1620 nBits |= WB_DEFBUTTON;
1621 SetStyle(nBits);
1623 else
1624 return Button::set_property(rKey, rValue);
1625 return true;
1628 void PushButton::ShowFocus(const tools::Rectangle& rRect)
1630 if (IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
1632 PushButtonValue aControlValue;
1633 aControlValue.mbIsAction = isAction();
1634 tools::Rectangle aInRect(Point(), GetOutputSizePixel());
1635 GetOutDev()->DrawNativeControl(ControlType::Pushbutton, ControlPart::Focus, aInRect,
1636 ControlState::FOCUSED, aControlValue, OUString());
1638 Button::ShowFocus(rRect);
1641 void OKButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1643 set_id("ok");
1644 PushButton::ImplInit( pParent, nStyle );
1646 SetText( GetStandardText( StandardButtonType::OK ) );
1649 OKButton::OKButton( vcl::Window* pParent, WinBits nStyle ) :
1650 PushButton( WindowType::OKBUTTON )
1652 ImplInit( pParent, nStyle );
1655 void OKButton::Click()
1657 // close parent if no link set
1658 if ( !GetClickHdl() )
1660 vcl::Window* pParent = getNonLayoutParent(this);
1661 if ( pParent->IsSystemWindow() )
1663 if ( pParent->IsDialog() )
1665 VclPtr<Dialog> xParent( static_cast<Dialog*>(pParent) );
1666 if ( xParent->IsInExecute() )
1667 xParent->EndDialog( RET_OK );
1668 // prevent recursive calls
1669 else if ( !xParent->IsInClose() )
1671 if ( pParent->GetStyle() & WB_CLOSEABLE )
1672 xParent->Close();
1675 else
1677 if ( pParent->GetStyle() & WB_CLOSEABLE )
1678 static_cast<SystemWindow*>(pParent)->Close();
1682 else
1684 PushButton::Click();
1688 void CancelButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1690 set_id("cancel");
1691 PushButton::ImplInit( pParent, nStyle );
1693 SetText( GetStandardText( StandardButtonType::Cancel ) );
1696 CancelButton::CancelButton( vcl::Window* pParent, WinBits nStyle ) :
1697 PushButton( WindowType::CANCELBUTTON )
1699 ImplInit( pParent, nStyle );
1702 void CancelButton::Click()
1704 // close parent if link not set
1705 if ( !GetClickHdl() )
1707 vcl::Window* pParent = getNonLayoutParent(this);
1708 if ( pParent->IsSystemWindow() )
1710 if ( pParent->IsDialog() )
1712 if ( static_cast<Dialog*>(pParent)->IsInExecute() )
1713 static_cast<Dialog*>(pParent)->EndDialog();
1714 // prevent recursive calls
1715 else if ( !static_cast<Dialog*>(pParent)->IsInClose() )
1717 if ( pParent->GetStyle() & WB_CLOSEABLE )
1718 static_cast<Dialog*>(pParent)->Close();
1721 else
1723 if ( pParent->GetStyle() & WB_CLOSEABLE )
1724 static_cast<SystemWindow*>(pParent)->Close();
1728 else
1730 PushButton::Click();
1734 CloseButton::CloseButton( vcl::Window* pParent, WinBits nStyle )
1735 : CancelButton(pParent, nStyle)
1737 SetText( GetStandardText( StandardButtonType::Close ) );
1740 void HelpButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1742 set_id("help");
1743 PushButton::ImplInit( pParent, nStyle | WB_NOPOINTERFOCUS );
1745 SetText( GetStandardText( StandardButtonType::Help ) );
1748 HelpButton::HelpButton( vcl::Window* pParent, WinBits nStyle ) :
1749 PushButton( WindowType::HELPBUTTON )
1751 ImplInit( pParent, nStyle );
1754 void HelpButton::Click()
1756 // trigger help if no link set
1757 if ( !GetClickHdl() )
1759 vcl::Window* pFocusWin = Application::GetFocusWindow();
1760 if ( !pFocusWin )
1761 pFocusWin = this;
1763 HelpEvent aEvt( pFocusWin->GetPointerPosPixel(), HelpEventMode::CONTEXT );
1764 pFocusWin->RequestHelp( aEvt );
1766 PushButton::Click();
1769 void HelpButton::StateChanged( StateChangedType nStateChange )
1771 // Hide when we have no help URL.
1772 if (comphelper::LibreOfficeKit::isActive() &&
1773 officecfg::Office::Common::Help::HelpRootURL::get().isEmpty())
1774 Hide();
1775 else
1776 PushButton::StateChanged(nStateChange);
1779 void RadioButton::ImplInitRadioButtonData()
1781 mbChecked = false;
1782 mbRadioCheck = true;
1783 mbStateChanged = false;
1786 void RadioButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
1788 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
1789 Button::ImplInit( pParent, nStyle, nullptr );
1791 ImplInitSettings( true );
1794 WinBits RadioButton::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
1796 if ( !(nStyle & WB_NOGROUP) &&
1797 (!pPrevWindow || (pPrevWindow->GetType() != WindowType::RADIOBUTTON)) )
1798 nStyle |= WB_GROUP;
1799 if ( !(nStyle & WB_NOTABSTOP) )
1801 if ( IsChecked() )
1802 nStyle |= WB_TABSTOP;
1803 else
1804 nStyle &= ~WB_TABSTOP;
1807 return nStyle;
1810 const vcl::Font& RadioButton::GetCanonicalFont( const StyleSettings& _rStyle ) const
1812 return _rStyle.GetRadioCheckFont();
1815 const Color& RadioButton::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
1817 return _rStyle.GetRadioCheckTextColor();
1820 void RadioButton::ImplInitSettings( bool bBackground )
1822 Button::ImplInitSettings();
1824 if ( !bBackground )
1825 return;
1827 vcl::Window* pParent = GetParent();
1828 if ( !IsControlBackground() &&
1829 (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) ) )
1831 EnableChildTransparentMode();
1832 SetParentClipMode( ParentClipMode::NoClip );
1833 SetPaintTransparent( true );
1834 SetBackground();
1835 if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
1836 mpWindowImpl->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
1838 else
1840 EnableChildTransparentMode( false );
1841 SetParentClipMode();
1842 SetPaintTransparent( false );
1844 if ( IsControlBackground() )
1845 SetBackground( GetControlBackground() );
1846 else
1847 SetBackground( pParent->GetBackground() );
1851 void RadioButton::ImplDrawRadioButtonState(vcl::RenderContext& rRenderContext)
1853 bool bNativeOK = false;
1855 // no native drawing for image radio buttons
1856 if (!maImage && rRenderContext.IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire))
1858 ImplControlValue aControlValue( mbChecked ? ButtonValue::On : ButtonValue::Off );
1859 tools::Rectangle aCtrlRect(maStateRect.TopLeft(), maStateRect.GetSize());
1860 ControlState nState = ControlState::NONE;
1862 if (GetButtonState() & DrawButtonFlags::Pressed)
1863 nState |= ControlState::PRESSED;
1864 if (HasFocus())
1865 nState |= ControlState::FOCUSED;
1866 if (GetButtonState() & DrawButtonFlags::Default)
1867 nState |= ControlState::DEFAULT;
1868 if (IsEnabled())
1869 nState |= ControlState::ENABLED;
1871 if (IsMouseOver() && maMouseRect.IsInside(GetPointerPosPixel()))
1872 nState |= ControlState::ROLLOVER;
1874 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Radiobutton, ControlPart::Entire, aCtrlRect,
1875 nState, aControlValue, OUString());
1878 if (bNativeOK)
1879 return;
1881 if (!maImage)
1883 DrawButtonFlags nStyle = GetButtonState();
1884 if (!IsEnabled())
1885 nStyle |= DrawButtonFlags::Disabled;
1886 if (mbChecked)
1887 nStyle |= DrawButtonFlags::Checked;
1888 Image aImage = GetRadioImage(rRenderContext.GetSettings(), nStyle);
1889 if (IsZoom())
1890 rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
1891 else
1892 rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
1894 else
1896 HideFocus();
1898 DecorationView aDecoView(&rRenderContext);
1899 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1900 tools::Rectangle aImageRect = maStateRect;
1901 Size aImageSize = maImage.GetSizePixel();
1902 bool bEnabled = IsEnabled();
1904 aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
1905 aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
1907 aImageRect.AdjustLeft( 1 );
1908 aImageRect.AdjustTop( 1 );
1909 aImageRect.AdjustRight( -1 );
1910 aImageRect.AdjustBottom( -1 );
1912 // display border and selection status
1913 aImageRect = aDecoView.DrawFrame(aImageRect, DrawFrameStyle::DoubleIn);
1914 if ((GetButtonState() & DrawButtonFlags::Pressed) || !bEnabled)
1915 rRenderContext.SetFillColor( rStyleSettings.GetFaceColor());
1916 else
1917 rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
1918 rRenderContext.SetLineColor();
1919 rRenderContext.DrawRect(aImageRect);
1921 // display image
1922 DrawImageFlags nImageStyle = DrawImageFlags::NONE;
1923 if (!bEnabled)
1924 nImageStyle |= DrawImageFlags::Disable;
1926 Image* pImage = &maImage;
1928 Point aImagePos(aImageRect.TopLeft());
1929 aImagePos.AdjustX((aImageRect.GetWidth() - aImageSize.Width()) / 2 );
1930 aImagePos.AdjustY((aImageRect.GetHeight() - aImageSize.Height()) / 2 );
1931 if (IsZoom())
1932 rRenderContext.DrawImage(aImagePos, aImageSize, *pImage, nImageStyle);
1933 else
1934 rRenderContext.DrawImage(aImagePos, *pImage, nImageStyle);
1936 aImageRect.AdjustLeft( 1 );
1937 aImageRect.AdjustTop( 1 );
1938 aImageRect.AdjustRight( -1 );
1939 aImageRect.AdjustBottom( -1 );
1941 ImplSetFocusRect(aImageRect);
1943 if (mbChecked)
1945 rRenderContext.SetLineColor(rStyleSettings.GetHighlightColor());
1946 rRenderContext.SetFillColor();
1947 if ((aImageSize.Width() >= 20) || (aImageSize.Height() >= 20))
1949 aImageRect.AdjustLeft( 1 );
1950 aImageRect.AdjustTop( 1 );
1951 aImageRect.AdjustRight( -1 );
1952 aImageRect.AdjustBottom( -1 );
1954 rRenderContext.DrawRect(aImageRect);
1955 aImageRect.AdjustLeft( 1 );
1956 aImageRect.AdjustTop( 1 );
1957 aImageRect.AdjustRight( -1 );
1958 aImageRect.AdjustBottom( -1 );
1959 rRenderContext.DrawRect(aImageRect);
1962 if (HasFocus())
1963 ShowFocus(ImplGetFocusRect());
1967 void RadioButton::ImplDraw( OutputDevice* pDev, DrawFlags nDrawFlags,
1968 const Point& rPos, const Size& rSize,
1969 const Size& rImageSize, tools::Rectangle& rStateRect,
1970 tools::Rectangle& rMouseRect )
1972 WinBits nWinStyle = GetStyle();
1973 OUString aText( GetText() );
1975 pDev->Push( PushFlags::CLIPREGION );
1976 pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
1978 // no image radio button
1979 if ( !maImage )
1981 if (!aText.isEmpty() || HasImage())
1983 DrawTextFlags nTextStyle = Button::ImplGetTextStyle( nWinStyle, nDrawFlags );
1985 const tools::Long nImageSep = GetDrawPixel( pDev, ImplGetImageToTextDistance() );
1986 Size aSize( rSize );
1987 Point aPos( rPos );
1988 aPos.AdjustX(rImageSize.Width() + nImageSep );
1990 // tdf#141761 Old (convenience?) adjustment of width may lead to empty
1991 // or negative(!) Size, that needs to be avoided. The coordinate context
1992 // is pixel-oriented (all Paints of Controls are, historically), so
1993 // the minimum width should be '1' Pixel.
1994 // Hint: nImageSep is based on Zoom (using Window::CalcZoom) and
1995 // MapModes (using Window::GetDrawPixel) - so potenially a wide range
1996 // of unpredictable values is possible
1997 const tools::Long nWidthAdjust(rImageSize.Width() + nImageSep);
1998 aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
2000 // if the text rect height is smaller than the height of the image
2001 // then for single lines the default should be centered text
2002 if( (nWinStyle & (WB_TOP|WB_VCENTER|WB_BOTTOM)) == 0 &&
2003 (rImageSize.Height() > rSize.Height() || ! (nWinStyle & WB_WORDBREAK) ) )
2005 nTextStyle &= ~DrawTextFlags(DrawTextFlags::Top|DrawTextFlags::Bottom);
2006 nTextStyle |= DrawTextFlags::VCenter;
2007 aSize.setHeight( rImageSize.Height() );
2010 ImplDrawAlignedImage( pDev, aPos, aSize, 1, nTextStyle );
2012 rMouseRect = tools::Rectangle(aPos, aSize);
2013 rMouseRect.SetLeft(rPos.X());
2015 rStateRect.SetLeft( rPos.X() );
2016 rStateRect.SetTop( rMouseRect.Top() );
2018 if ( aSize.Height() > rImageSize.Height() )
2019 rStateRect.AdjustTop(( aSize.Height() - rImageSize.Height() ) / 2 );
2020 else
2022 rStateRect.AdjustTop( -(( rImageSize.Height() - aSize.Height() ) / 2) );
2023 if( rStateRect.Top() < 0 )
2024 rStateRect.SetTop( 0 );
2027 rStateRect.SetRight( rStateRect.Left() + rImageSize.Width()-1 );
2028 rStateRect.SetBottom( rStateRect.Top() + rImageSize.Height()-1 );
2030 if ( rStateRect.Bottom() > rMouseRect.Bottom() )
2031 rMouseRect.SetBottom( rStateRect.Bottom() );
2033 else
2035 rStateRect.SetLeft( rPos.X() );
2036 if ( nWinStyle & WB_VCENTER )
2037 rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
2038 else if ( nWinStyle & WB_BOTTOM )
2039 rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() ); //-1;
2040 else
2041 rStateRect.SetTop( rPos.Y() );
2042 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
2043 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
2044 rMouseRect = rStateRect;
2046 ImplSetFocusRect( rStateRect );
2049 else
2051 bool bTopImage = (nWinStyle & WB_TOP) != 0;
2052 Size aImageSize = maImage.GetSizePixel();
2053 tools::Rectangle aImageRect( rPos, rSize );
2054 tools::Long nTextHeight = pDev->GetTextHeight();
2055 tools::Long nTextWidth = pDev->GetCtrlTextWidth( aText );
2057 // calculate position and sizes
2058 if (!aText.isEmpty())
2060 Size aTmpSize( (aImageSize.Width()+8), (aImageSize.Height()+8) );
2061 if ( bTopImage )
2063 aImageRect.SetLeft( (rSize.Width()-aTmpSize.Width())/2 );
2064 aImageRect.SetTop( (rSize.Height()-(aTmpSize.Height()+nTextHeight+6))/2 );
2066 else
2067 aImageRect.SetTop( (rSize.Height()-aTmpSize.Height())/2 );
2069 aImageRect.SetRight( aImageRect.Left()+aTmpSize.Width() );
2070 aImageRect.SetBottom( aImageRect.Top()+aTmpSize.Height() );
2072 // display text
2073 Point aTxtPos = rPos;
2074 if ( bTopImage )
2076 aTxtPos.AdjustX((rSize.Width()-nTextWidth)/2 );
2077 aTxtPos.AdjustY(aImageRect.Bottom()+6 );
2079 else
2081 aTxtPos.AdjustX(aImageRect.Right()+8 );
2082 aTxtPos.AdjustY((rSize.Height()-nTextHeight)/2 );
2084 pDev->DrawCtrlText( aTxtPos, aText, 0, aText.getLength() );
2087 rMouseRect = aImageRect;
2088 rStateRect = aImageRect;
2091 pDev->Pop();
2094 void RadioButton::ImplDrawRadioButton(vcl::RenderContext& rRenderContext)
2096 HideFocus();
2098 Size aImageSize;
2099 if (!maImage)
2100 aImageSize = ImplGetRadioImageSize();
2101 else
2102 aImageSize = maImage.GetSizePixel();
2104 aImageSize.setWidth( CalcZoom(aImageSize.Width()) );
2105 aImageSize.setHeight( CalcZoom(aImageSize.Height()) );
2107 // Draw control text
2108 ImplDraw(&rRenderContext, DrawFlags::NONE, Point(), GetOutputSizePixel(),
2109 aImageSize, maStateRect, maMouseRect);
2111 if (!maImage && HasFocus())
2112 ShowFocus(ImplGetFocusRect());
2114 ImplDrawRadioButtonState(rRenderContext);
2117 void RadioButton::group(RadioButton &rOther)
2119 if (&rOther == this)
2120 return;
2122 if (!m_xGroup)
2124 m_xGroup = std::make_shared<std::vector<VclPtr<RadioButton> >>();
2125 m_xGroup->push_back(this);
2128 auto aFind = std::find(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(&rOther));
2129 if (aFind == m_xGroup->end())
2131 m_xGroup->push_back(&rOther);
2133 if (rOther.m_xGroup)
2135 std::vector< VclPtr<RadioButton> > aOthers(rOther.GetRadioButtonGroup(false));
2136 //make all members of the group share the same button group
2137 for (auto const& elem : aOthers)
2139 aFind = std::find(m_xGroup->begin(), m_xGroup->end(), elem);
2140 if (aFind == m_xGroup->end())
2141 m_xGroup->push_back(elem);
2145 //make all members of the group share the same button group
2146 for (VclPtr<RadioButton> const & pButton : *m_xGroup)
2148 pButton->m_xGroup = m_xGroup;
2152 //if this one is checked, uncheck all the others
2153 if (mbChecked)
2154 ImplUncheckAllOther();
2157 std::vector< VclPtr<RadioButton> > RadioButton::GetRadioButtonGroup(bool bIncludeThis) const
2159 if (m_xGroup)
2161 if (bIncludeThis)
2162 return *m_xGroup;
2163 std::vector< VclPtr<RadioButton> > aGroup;
2164 for (VclPtr<RadioButton> const & pRadioButton : *m_xGroup)
2166 if (pRadioButton == this)
2167 continue;
2168 aGroup.push_back(pRadioButton);
2170 return aGroup;
2173 std::vector<VclPtr<RadioButton>> aGroup;
2174 if (mbUsesExplicitGroup)
2175 return aGroup;
2177 //old-school
2179 // go back to first in group;
2180 vcl::Window* pFirst = const_cast<RadioButton*>(this);
2181 while( ( pFirst->GetStyle() & WB_GROUP ) == 0 )
2183 vcl::Window* pWindow = pFirst->GetWindow( GetWindowType::Prev );
2184 if( pWindow )
2185 pFirst = pWindow;
2186 else
2187 break;
2189 // insert radiobuttons up to next group
2192 if( pFirst->GetType() == WindowType::RADIOBUTTON )
2194 if( pFirst != this || bIncludeThis )
2195 aGroup.emplace_back(static_cast<RadioButton*>(pFirst) );
2197 pFirst = pFirst->GetWindow( GetWindowType::Next );
2198 } while( pFirst && ( ( pFirst->GetStyle() & WB_GROUP ) == 0 ) );
2200 return aGroup;
2203 void RadioButton::ImplUncheckAllOther()
2205 mpWindowImpl->mnStyle |= WB_TABSTOP;
2207 std::vector<VclPtr<RadioButton> > aGroup(GetRadioButtonGroup(false));
2208 // iterate over radio button group and checked buttons
2209 for (VclPtr<RadioButton>& pWindow : aGroup)
2211 if ( pWindow->IsChecked() )
2213 pWindow->SetState( false );
2214 if ( pWindow->IsDisposed() )
2215 return;
2218 // not inside if clause to always remove wrongly set WB_TABSTOPS
2219 pWindow->mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2223 void RadioButton::ImplCallClick( bool bGrabFocus, GetFocusFlags nFocusFlags )
2225 mbStateChanged = !mbChecked;
2226 mbChecked = true;
2227 mpWindowImpl->mnStyle |= WB_TABSTOP;
2228 Invalidate();
2229 VclPtr<vcl::Window> xWindow = this;
2230 if ( mbRadioCheck )
2231 ImplUncheckAllOther();
2232 if ( xWindow->IsDisposed() )
2233 return;
2234 if ( bGrabFocus )
2235 ImplGrabFocus( nFocusFlags );
2236 if ( xWindow->IsDisposed() )
2237 return;
2238 if ( mbStateChanged )
2239 Toggle();
2240 if ( xWindow->IsDisposed() )
2241 return;
2242 Click();
2243 if ( xWindow->IsDisposed() )
2244 return;
2245 mbStateChanged = false;
2248 RadioButton::RadioButton(vcl::Window* pParent, bool bUsesExplicitGroup, WinBits nStyle)
2249 : Button(WindowType::RADIOBUTTON)
2250 , mbUsesExplicitGroup(bUsesExplicitGroup)
2252 ImplInitRadioButtonData();
2253 ImplInit( pParent, nStyle );
2256 RadioButton::~RadioButton()
2258 disposeOnce();
2261 void RadioButton::dispose()
2263 if (m_xGroup)
2265 m_xGroup->erase(std::remove(m_xGroup->begin(), m_xGroup->end(), VclPtr<RadioButton>(this)),
2266 m_xGroup->end());
2267 m_xGroup.reset();
2269 Button::dispose();
2272 void RadioButton::MouseButtonDown( const MouseEvent& rMEvt )
2274 if ( rMEvt.IsLeft() && maMouseRect.IsInside( rMEvt.GetPosPixel() ) )
2276 GetButtonState() |= DrawButtonFlags::Pressed;
2277 Invalidate();
2278 StartTracking();
2279 return;
2282 Button::MouseButtonDown( rMEvt );
2285 void RadioButton::Tracking( const TrackingEvent& rTEvt )
2287 if ( rTEvt.IsTrackingEnded() )
2289 if ( GetButtonState() & DrawButtonFlags::Pressed )
2291 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
2292 GrabFocus();
2294 GetButtonState() &= ~DrawButtonFlags::Pressed;
2296 // do not call click handler if aborted
2297 if ( !rTEvt.IsTrackingCanceled() )
2298 ImplCallClick();
2299 else
2301 Invalidate();
2305 else
2307 if ( maMouseRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() ) )
2309 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
2311 GetButtonState() |= DrawButtonFlags::Pressed;
2312 Invalidate();
2315 else
2317 if ( GetButtonState() & DrawButtonFlags::Pressed )
2319 GetButtonState() &= ~DrawButtonFlags::Pressed;
2320 Invalidate();
2326 void RadioButton::KeyInput( const KeyEvent& rKEvt )
2328 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
2330 if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
2332 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
2334 GetButtonState() |= DrawButtonFlags::Pressed;
2335 Invalidate();
2338 else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
2340 GetButtonState() &= ~DrawButtonFlags::Pressed;
2341 Invalidate();
2343 else
2344 Button::KeyInput( rKEvt );
2347 void RadioButton::KeyUp( const KeyEvent& rKEvt )
2349 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
2351 if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
2353 GetButtonState() &= ~DrawButtonFlags::Pressed;
2354 ImplCallClick();
2356 else
2357 Button::KeyUp( rKEvt );
2360 void RadioButton::FillLayoutData() const
2362 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
2363 const_cast<RadioButton*>(this)->Invalidate();
2366 void RadioButton::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
2368 ImplDrawRadioButton(rRenderContext);
2371 void RadioButton::Draw( OutputDevice* pDev, const Point& rPos,
2372 DrawFlags nFlags )
2374 if ( !maImage )
2376 MapMode aResMapMode( MapUnit::Map100thMM );
2377 Point aPos = pDev->LogicToPixel( rPos );
2378 Size aSize = GetSizePixel();
2379 Size aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
2380 Size aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
2381 Size aBrd2Size = pDev->LogicToPixel( Size( 60, 60 ), aResMapMode );
2382 vcl::Font aFont = GetDrawPixelFont( pDev );
2383 tools::Rectangle aStateRect;
2384 tools::Rectangle aMouseRect;
2386 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
2387 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
2388 aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
2389 aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
2390 aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
2391 aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
2393 if ( !aBrd1Size.Width() )
2394 aBrd1Size.setWidth( 1 );
2395 if ( !aBrd1Size.Height() )
2396 aBrd1Size.setHeight( 1 );
2397 if ( !aBrd2Size.Width() )
2398 aBrd2Size.setWidth( 1 );
2399 if ( !aBrd2Size.Height() )
2400 aBrd2Size.setHeight( 1 );
2402 pDev->Push();
2403 pDev->SetMapMode();
2404 pDev->SetFont( aFont );
2405 if ( nFlags & DrawFlags::Mono )
2406 pDev->SetTextColor( COL_BLACK );
2407 else
2408 pDev->SetTextColor( GetTextColor() );
2409 pDev->SetTextFillColor();
2411 ImplDraw( pDev, nFlags, aPos, aSize,
2412 aImageSize, aStateRect, aMouseRect );
2414 Point aCenterPos = aStateRect.Center();
2415 tools::Long nRadX = aImageSize.Width()/2;
2416 tools::Long nRadY = aImageSize.Height()/2;
2418 pDev->SetLineColor();
2419 pDev->SetFillColor( COL_BLACK );
2420 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2421 nRadX -= aBrd1Size.Width();
2422 nRadY -= aBrd1Size.Height();
2423 pDev->SetFillColor( COL_WHITE );
2424 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2425 if ( mbChecked )
2427 nRadX -= aBrd1Size.Width();
2428 nRadY -= aBrd1Size.Height();
2429 if ( !nRadX )
2430 nRadX = 1;
2431 if ( !nRadY )
2432 nRadY = 1;
2433 pDev->SetFillColor( COL_BLACK );
2434 pDev->DrawPolygon( tools::Polygon( aCenterPos, nRadX, nRadY ) );
2437 pDev->Pop();
2439 else
2441 OSL_FAIL( "RadioButton::Draw() - not implemented for RadioButton with Image" );
2445 void RadioButton::Resize()
2447 Control::Resize();
2448 Invalidate();
2451 void RadioButton::GetFocus()
2453 ShowFocus( ImplGetFocusRect() );
2454 SetInputContext( InputContext( GetFont() ) );
2455 Button::GetFocus();
2458 void RadioButton::LoseFocus()
2460 if ( GetButtonState() & DrawButtonFlags::Pressed )
2462 GetButtonState() &= ~DrawButtonFlags::Pressed;
2463 Invalidate();
2466 HideFocus();
2467 Button::LoseFocus();
2470 void RadioButton::StateChanged( StateChangedType nType )
2472 Button::StateChanged( nType );
2474 if ( nType == StateChangedType::State )
2476 if ( IsReallyVisible() && IsUpdateMode() )
2477 Invalidate( maStateRect );
2479 else if ( (nType == StateChangedType::Enable) ||
2480 (nType == StateChangedType::Text) ||
2481 (nType == StateChangedType::Data) ||
2482 (nType == StateChangedType::UpdateMode) )
2484 if ( IsUpdateMode() )
2485 Invalidate();
2487 else if ( nType == StateChangedType::Style )
2489 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
2491 if ( (GetPrevStyle() & RADIOBUTTON_VIEW_STYLE) !=
2492 (GetStyle() & RADIOBUTTON_VIEW_STYLE) )
2494 if ( IsUpdateMode() )
2495 Invalidate();
2498 else if ( (nType == StateChangedType::Zoom) ||
2499 (nType == StateChangedType::ControlFont) )
2501 ImplInitSettings( false );
2502 Invalidate();
2504 else if ( nType == StateChangedType::ControlForeground )
2506 ImplInitSettings( false );
2507 Invalidate();
2509 else if ( nType == StateChangedType::ControlBackground )
2511 ImplInitSettings( true );
2512 Invalidate();
2516 void RadioButton::DataChanged( const DataChangedEvent& rDCEvt )
2518 Button::DataChanged( rDCEvt );
2520 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
2521 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
2522 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
2523 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
2525 ImplInitSettings( true );
2526 Invalidate();
2530 bool RadioButton::PreNotify( NotifyEvent& rNEvt )
2532 if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
2534 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
2535 if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
2537 // trigger redraw if mouse over state has changed
2538 if( IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Entire) )
2540 if (maMouseRect.IsInside(GetPointerPosPixel()) != maMouseRect.IsInside(GetLastPointerPosPixel()) ||
2541 pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
2543 Invalidate( maStateRect );
2549 return Button::PreNotify(rNEvt);
2552 void RadioButton::Toggle()
2554 ImplCallEventListenersAndHandler( VclEventId::RadiobuttonToggle, [this] () { maToggleHdl.Call(*this); } );
2557 void RadioButton::SetModeRadioImage( const Image& rImage )
2559 if ( rImage != maImage )
2561 maImage = rImage;
2562 CompatStateChanged( StateChangedType::Data );
2563 queue_resize();
2568 void RadioButton::SetState( bool bCheck )
2570 // carry the TabStop flag along correctly
2571 if ( bCheck )
2572 mpWindowImpl->mnStyle |= WB_TABSTOP;
2573 else
2574 mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2576 if ( mbChecked != bCheck )
2578 mbChecked = bCheck;
2579 CompatStateChanged( StateChangedType::State );
2580 Toggle();
2584 bool RadioButton::set_property(const OString &rKey, const OUString &rValue)
2586 if (rKey == "active")
2587 SetState(toBool(rValue));
2588 else if (rKey == "image-position")
2590 WinBits nBits = GetStyle();
2591 if (rValue == "left")
2593 nBits &= ~(WB_CENTER | WB_RIGHT);
2594 nBits |= WB_LEFT;
2596 else if (rValue == "right")
2598 nBits &= ~(WB_CENTER | WB_LEFT);
2599 nBits |= WB_RIGHT;
2601 else if (rValue == "top")
2603 nBits &= ~(WB_VCENTER | WB_BOTTOM);
2604 nBits |= WB_TOP;
2606 else if (rValue == "bottom")
2608 nBits &= ~(WB_VCENTER | WB_TOP);
2609 nBits |= WB_BOTTOM;
2611 //It's rather mad to have to set these bits when there is the other
2612 //image align. Looks like e.g. the radiobuttons etc weren't converted
2613 //over to image align fully.
2614 SetStyle(nBits);
2615 //Deliberate to set the sane ImageAlign property
2616 return Button::set_property(rKey, rValue);
2618 else
2619 return Button::set_property(rKey, rValue);
2620 return true;
2623 void RadioButton::Check( bool bCheck )
2625 // TabStop-Flag richtig mitfuehren
2626 if ( bCheck )
2627 mpWindowImpl->mnStyle |= WB_TABSTOP;
2628 else
2629 mpWindowImpl->mnStyle &= ~WB_TABSTOP;
2631 if ( mbChecked == bCheck )
2632 return;
2634 mbChecked = bCheck;
2635 VclPtr<vcl::Window> xWindow = this;
2636 CompatStateChanged( StateChangedType::State );
2637 if ( xWindow->IsDisposed() )
2638 return;
2639 if ( bCheck && mbRadioCheck )
2640 ImplUncheckAllOther();
2641 if ( xWindow->IsDisposed() )
2642 return;
2643 Toggle();
2646 tools::Long RadioButton::ImplGetImageToTextDistance() const
2648 // 4 pixels, but take zoom into account, so the text doesn't "jump" relative to surrounding elements,
2649 // which might have been aligned with the text of the check box
2650 return CalcZoom( 4 );
2653 Size RadioButton::ImplGetRadioImageSize() const
2655 Size aSize;
2656 bool bDefaultSize = true;
2657 if( IsNativeControlSupported( ControlType::Radiobutton, ControlPart::Entire ) )
2659 ImplControlValue aControlValue;
2660 tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
2661 tools::Rectangle aBoundingRgn, aContentRgn;
2663 // get native size of a radio button
2664 if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
2665 ControlState::DEFAULT|ControlState::ENABLED,
2666 aControlValue,
2667 aBoundingRgn, aContentRgn ) )
2669 aSize = aContentRgn.GetSize();
2670 bDefaultSize = false;
2673 if( bDefaultSize )
2674 aSize = GetRadioImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
2675 return aSize;
2678 static void LoadThemedImageList(const StyleSettings &rStyleSettings,
2679 std::vector<Image>& rList, const std::vector<OUString> &rResources)
2681 Color aColorAry1[6];
2682 Color aColorAry2[6];
2683 aColorAry1[0] = Color( 0xC0, 0xC0, 0xC0 );
2684 aColorAry1[1] = Color( 0xFF, 0xFF, 0x00 );
2685 aColorAry1[2] = Color( 0xFF, 0xFF, 0xFF );
2686 aColorAry1[3] = Color( 0x80, 0x80, 0x80 );
2687 aColorAry1[4] = Color( 0x00, 0x00, 0x00 );
2688 aColorAry1[5] = Color( 0x00, 0xFF, 0x00 );
2689 aColorAry2[0] = rStyleSettings.GetFaceColor();
2690 aColorAry2[1] = rStyleSettings.GetWindowColor();
2691 aColorAry2[2] = rStyleSettings.GetLightColor();
2692 aColorAry2[3] = rStyleSettings.GetShadowColor();
2693 aColorAry2[4] = rStyleSettings.GetDarkShadowColor();
2694 aColorAry2[5] = rStyleSettings.GetWindowTextColor();
2696 static_assert( sizeof(aColorAry1) == sizeof(aColorAry2), "aColorAry1 must match aColorAry2" );
2698 for (const auto &a : rResources)
2700 BitmapEx aBmpEx(a);
2701 aBmpEx.Replace(aColorAry1, aColorAry2, SAL_N_ELEMENTS(aColorAry1));
2702 rList.emplace_back(aBmpEx);
2706 Image RadioButton::GetRadioImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
2708 ImplSVData* pSVData = ImplGetSVData();
2709 const StyleSettings& rStyleSettings = rSettings.GetStyleSettings();
2710 sal_uInt16 nStyle = 0;
2712 if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
2713 nStyle = STYLE_RADIOBUTTON_MONO;
2715 if ( pSVData->maCtrlData.maRadioImgList.empty() ||
2716 (pSVData->maCtrlData.mnRadioStyle != nStyle) ||
2717 (pSVData->maCtrlData.mnLastRadioFColor != rStyleSettings.GetFaceColor()) ||
2718 (pSVData->maCtrlData.mnLastRadioWColor != rStyleSettings.GetWindowColor()) ||
2719 (pSVData->maCtrlData.mnLastRadioLColor != rStyleSettings.GetLightColor()) )
2721 pSVData->maCtrlData.maRadioImgList.clear();
2723 pSVData->maCtrlData.mnLastRadioFColor = rStyleSettings.GetFaceColor();
2724 pSVData->maCtrlData.mnLastRadioWColor = rStyleSettings.GetWindowColor();
2725 pSVData->maCtrlData.mnLastRadioLColor = rStyleSettings.GetLightColor();
2727 std::vector<OUString> aResources;
2728 if (nStyle)
2730 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO1);
2731 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO2);
2732 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO3);
2733 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO4);
2734 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO5);
2735 aResources.emplace_back(SV_RESID_BITMAP_RADIOMONO6);
2737 else
2739 aResources.emplace_back(SV_RESID_BITMAP_RADIO1);
2740 aResources.emplace_back(SV_RESID_BITMAP_RADIO2);
2741 aResources.emplace_back(SV_RESID_BITMAP_RADIO3);
2742 aResources.emplace_back(SV_RESID_BITMAP_RADIO4);
2743 aResources.emplace_back(SV_RESID_BITMAP_RADIO5);
2744 aResources.emplace_back(SV_RESID_BITMAP_RADIO6);
2746 LoadThemedImageList( rStyleSettings, pSVData->maCtrlData.maRadioImgList, aResources);
2747 pSVData->maCtrlData.mnRadioStyle = nStyle;
2750 sal_uInt16 nIndex;
2751 if ( nFlags & DrawButtonFlags::Disabled )
2753 if ( nFlags & DrawButtonFlags::Checked )
2754 nIndex = 5;
2755 else
2756 nIndex = 4;
2758 else if ( nFlags & DrawButtonFlags::Pressed )
2760 if ( nFlags & DrawButtonFlags::Checked )
2761 nIndex = 3;
2762 else
2763 nIndex = 2;
2765 else
2767 if ( nFlags & DrawButtonFlags::Checked )
2768 nIndex = 1;
2769 else
2770 nIndex = 0;
2772 return pSVData->maCtrlData.maRadioImgList[nIndex];
2775 void RadioButton::ImplAdjustNWFSizes()
2777 Push( PushFlags::MAPMODE );
2778 SetMapMode(MapMode(MapUnit::MapPixel));
2780 ImplControlValue aControlValue;
2781 Size aCurSize( GetSizePixel() );
2782 tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
2783 tools::Rectangle aBoundingRgn, aContentRgn;
2785 // get native size of a radiobutton
2786 if( GetNativeControlRegion( ControlType::Radiobutton, ControlPart::Entire, aCtrlRegion,
2787 ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
2788 aBoundingRgn, aContentRgn ) )
2790 Size aSize = aContentRgn.GetSize();
2792 if( aSize.Height() > aCurSize.Height() )
2794 aCurSize.setHeight( aSize.Height() );
2795 SetSizePixel( aCurSize );
2799 Pop();
2802 Size RadioButton::CalcMinimumSize(tools::Long nMaxWidth) const
2804 Size aSize;
2805 if ( !maImage )
2806 aSize = ImplGetRadioImageSize();
2807 else
2809 aSize = maImage.GetSizePixel();
2810 aSize.AdjustWidth(8);
2811 aSize.AdjustHeight(8);
2814 if (Button::HasImage())
2816 Size aImgSize = GetModeImage().GetSizePixel();
2817 aSize = Size(std::max(aImgSize.Width(), aSize.Width()),
2818 std::max(aImgSize.Height(), aSize.Height()));
2821 OUString aText = GetText();
2822 if (!aText.isEmpty())
2824 bool bTopImage = (GetStyle() & WB_TOP) != 0;
2826 Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
2827 aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
2829 aSize.AdjustWidth(2 ); // for focus rect
2831 if (!bTopImage)
2833 aSize.AdjustWidth(ImplGetImageToTextDistance() );
2834 aSize.AdjustWidth(aTextSize.Width() );
2835 if ( aSize.Height() < aTextSize.Height() )
2836 aSize.setHeight( aTextSize.Height() );
2838 else
2840 aSize.AdjustHeight(6 );
2841 aSize.AdjustHeight(GetTextHeight() );
2842 if ( aSize.Width() < aTextSize.Width() )
2843 aSize.setWidth( aTextSize.Width() );
2847 return CalcWindowSize( aSize );
2850 Size RadioButton::GetOptimalSize() const
2852 return CalcMinimumSize();
2855 void RadioButton::ShowFocus(const tools::Rectangle& rRect)
2857 if (IsNativeControlSupported(ControlType::Radiobutton, ControlPart::Focus))
2859 ImplControlValue aControlValue;
2860 tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
2862 aInRect.SetLeft( rRect.Left() ); // exclude the radio element itself from the focusrect
2864 DrawNativeControl(ControlType::Radiobutton, ControlPart::Focus, aInRect,
2865 ControlState::FOCUSED, aControlValue, OUString());
2867 Button::ShowFocus(rRect);
2870 void RadioButton::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
2872 Button::DumpAsPropertyTree(rJsonWriter);
2873 rJsonWriter.put("checked", IsChecked());
2876 FactoryFunction RadioButton::GetUITestFactory() const
2878 return RadioButtonUIObject::create;
2881 void CheckBox::ImplInitCheckBoxData()
2883 meState = TRISTATE_FALSE;
2884 mbTriState = false;
2887 void CheckBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
2889 nStyle = ImplInitStyle(getPreviousSibling(pParent), nStyle);
2890 Button::ImplInit( pParent, nStyle, nullptr );
2892 ImplInitSettings( true );
2895 WinBits CheckBox::ImplInitStyle( const vcl::Window* pPrevWindow, WinBits nStyle )
2897 if ( !(nStyle & WB_NOTABSTOP) )
2898 nStyle |= WB_TABSTOP;
2899 if ( !(nStyle & WB_NOGROUP) &&
2900 (!pPrevWindow || (pPrevWindow->GetType() != WindowType::CHECKBOX)) )
2901 nStyle |= WB_GROUP;
2902 return nStyle;
2905 const vcl::Font& CheckBox::GetCanonicalFont( const StyleSettings& _rStyle ) const
2907 return _rStyle.GetRadioCheckFont();
2910 const Color& CheckBox::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
2912 return _rStyle.GetRadioCheckTextColor();
2915 void CheckBox::ImplInitSettings( bool bBackground )
2917 Button::ImplInitSettings();
2919 if ( !bBackground )
2920 return;
2922 vcl::Window* pParent = GetParent();
2923 if ( !IsControlBackground() &&
2924 (pParent->IsChildTransparentModeEnabled() || IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) ) )
2926 EnableChildTransparentMode();
2927 SetParentClipMode( ParentClipMode::NoClip );
2928 SetPaintTransparent( true );
2929 SetBackground();
2930 if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
2931 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
2933 else
2935 EnableChildTransparentMode( false );
2936 SetParentClipMode();
2937 SetPaintTransparent( false );
2939 if ( IsControlBackground() )
2940 SetBackground( GetControlBackground() );
2941 else
2942 SetBackground( pParent->GetBackground() );
2946 void CheckBox::ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext)
2948 bool bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire);
2949 if (bNativeOK)
2951 ImplControlValue aControlValue(meState == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
2952 tools::Rectangle aCtrlRegion(maStateRect);
2953 ControlState nState = ControlState::NONE;
2955 if (HasFocus())
2956 nState |= ControlState::FOCUSED;
2957 if (GetButtonState() & DrawButtonFlags::Default)
2958 nState |= ControlState::DEFAULT;
2959 if (GetButtonState() & DrawButtonFlags::Pressed)
2960 nState |= ControlState::PRESSED;
2961 if (IsEnabled())
2962 nState |= ControlState::ENABLED;
2964 if (meState == TRISTATE_TRUE)
2965 aControlValue.setTristateVal(ButtonValue::On);
2966 else if (meState == TRISTATE_INDET)
2967 aControlValue.setTristateVal(ButtonValue::Mixed);
2969 if (IsMouseOver() && maMouseRect.IsInside(GetPointerPosPixel()))
2970 nState |= ControlState::ROLLOVER;
2972 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
2973 nState, aControlValue, OUString());
2976 if (bNativeOK)
2977 return;
2979 DrawButtonFlags nStyle = GetButtonState();
2980 if (!IsEnabled())
2981 nStyle |= DrawButtonFlags::Disabled;
2982 if (meState == TRISTATE_INDET)
2983 nStyle |= DrawButtonFlags::DontKnow;
2984 else if (meState == TRISTATE_TRUE)
2985 nStyle |= DrawButtonFlags::Checked;
2986 Image aImage = GetCheckImage(GetSettings(), nStyle);
2987 if (IsZoom())
2988 rRenderContext.DrawImage(maStateRect.TopLeft(), maStateRect.GetSize(), aImage);
2989 else
2990 rRenderContext.DrawImage(maStateRect.TopLeft(), aImage);
2993 void CheckBox::ImplDraw( OutputDevice* pDev, DrawFlags nDrawFlags,
2994 const Point& rPos, const Size& rSize,
2995 const Size& rImageSize, tools::Rectangle& rStateRect,
2996 tools::Rectangle& rMouseRect )
2998 WinBits nWinStyle = GetStyle();
2999 OUString aText( GetText() );
3001 pDev->Push( PushFlags::CLIPREGION | PushFlags::LINECOLOR );
3002 pDev->IntersectClipRegion( tools::Rectangle( rPos, rSize ) );
3004 if (!aText.isEmpty() || HasImage())
3006 DrawTextFlags nTextStyle = Button::ImplGetTextStyle( nWinStyle, nDrawFlags );
3008 const tools::Long nImageSep = GetDrawPixel( pDev, ImplGetImageToTextDistance() );
3009 Size aSize( rSize );
3010 Point aPos( rPos );
3011 aPos.AdjustX(rImageSize.Width() + nImageSep );
3013 // tdf#141761 See comment with same ID above
3014 const tools::Long nWidthAdjust(rImageSize.Width() + nImageSep);
3015 aSize.setWidth(std::max(static_cast<tools::Long>(1), aSize.getWidth() - nWidthAdjust));
3017 // if the text rect height is smaller than the height of the image
3018 // then for single lines the default should be centered text
3019 if( (nWinStyle & (WB_TOP|WB_VCENTER|WB_BOTTOM)) == 0 &&
3020 (rImageSize.Height() > rSize.Height() || ! (nWinStyle & WB_WORDBREAK) ) )
3022 nTextStyle &= ~DrawTextFlags(DrawTextFlags::Top|DrawTextFlags::Bottom);
3023 nTextStyle |= DrawTextFlags::VCenter;
3024 aSize.setHeight( rImageSize.Height() );
3027 ImplDrawAlignedImage( pDev, aPos, aSize, 1, nTextStyle );
3029 rMouseRect = tools::Rectangle( aPos, aSize );
3030 rMouseRect.SetLeft( rPos.X() );
3032 rStateRect.SetLeft( rPos.X() );
3033 rStateRect.SetTop( rMouseRect.Top() );
3035 if ( aSize.Height() > rImageSize.Height() )
3036 rStateRect.AdjustTop(( aSize.Height() - rImageSize.Height() ) / 2 );
3037 else
3039 rStateRect.AdjustTop( -(( rImageSize.Height() - aSize.Height() ) / 2) );
3040 if( rStateRect.Top() < 0 )
3041 rStateRect.SetTop( 0 );
3044 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
3045 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
3046 if ( rStateRect.Bottom() > rMouseRect.Bottom() )
3047 rMouseRect.SetBottom( rStateRect.Bottom() );
3049 else
3051 rStateRect.SetLeft( rPos.X() );
3052 if ( nWinStyle & WB_VCENTER )
3053 rStateRect.SetTop( rPos.Y()+((rSize.Height()-rImageSize.Height())/2) );
3054 else if ( nWinStyle & WB_BOTTOM )
3055 rStateRect.SetTop( rPos.Y()+rSize.Height()-rImageSize.Height() );
3056 else
3057 rStateRect.SetTop( rPos.Y() );
3058 rStateRect.SetRight( rStateRect.Left()+rImageSize.Width()-1 );
3059 rStateRect.SetBottom( rStateRect.Top()+rImageSize.Height()-1 );
3060 // provide space for focusrect
3061 // note: this assumes that the control's size was adjusted
3062 // accordingly in Get/LoseFocus, so the onscreen position won't change
3063 if( HasFocus() )
3064 rStateRect.Move( 1, 1 );
3065 rMouseRect = rStateRect;
3067 ImplSetFocusRect( rStateRect );
3070 pDev->Pop();
3073 void CheckBox::ImplDrawCheckBox(vcl::RenderContext& rRenderContext)
3075 Size aImageSize = ImplGetCheckImageSize();
3076 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
3077 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
3079 HideFocus();
3081 ImplDraw(&rRenderContext, DrawFlags::NONE, Point(), GetOutputSizePixel(),
3082 aImageSize, maStateRect, maMouseRect);
3084 ImplDrawCheckBoxState(rRenderContext);
3085 if (HasFocus())
3086 ShowFocus(ImplGetFocusRect());
3089 void CheckBox::ImplCheck()
3091 TriState eNewState;
3092 if ( meState == TRISTATE_FALSE )
3093 eNewState = TRISTATE_TRUE;
3094 else if ( !mbTriState )
3095 eNewState = TRISTATE_FALSE;
3096 else if ( meState == TRISTATE_TRUE )
3097 eNewState = TRISTATE_INDET;
3098 else
3099 eNewState = TRISTATE_FALSE;
3100 meState = eNewState;
3102 VclPtr<vcl::Window> xWindow = this;
3103 Invalidate();
3104 Toggle();
3105 if ( xWindow->IsDisposed() )
3106 return;
3107 Click();
3110 CheckBox::CheckBox( vcl::Window* pParent, WinBits nStyle ) :
3111 Button( WindowType::CHECKBOX )
3113 ImplInitCheckBoxData();
3114 ImplInit( pParent, nStyle );
3117 void CheckBox::MouseButtonDown( const MouseEvent& rMEvt )
3119 if ( rMEvt.IsLeft() && maMouseRect.IsInside( rMEvt.GetPosPixel() ) )
3121 GetButtonState() |= DrawButtonFlags::Pressed;
3122 Invalidate();
3123 StartTracking();
3124 return;
3127 Button::MouseButtonDown( rMEvt );
3130 void CheckBox::Tracking( const TrackingEvent& rTEvt )
3132 if ( rTEvt.IsTrackingEnded() )
3134 if ( GetButtonState() & DrawButtonFlags::Pressed )
3136 if ( !(GetStyle() & WB_NOPOINTERFOCUS) && !rTEvt.IsTrackingCanceled() )
3137 GrabFocus();
3139 GetButtonState() &= ~DrawButtonFlags::Pressed;
3141 // do not call click handler if aborted
3142 if ( !rTEvt.IsTrackingCanceled() )
3143 ImplCheck();
3144 else
3146 Invalidate();
3150 else
3152 if ( maMouseRect.IsInside( rTEvt.GetMouseEvent().GetPosPixel() ) )
3154 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
3156 GetButtonState() |= DrawButtonFlags::Pressed;
3157 Invalidate();
3160 else
3162 if ( GetButtonState() & DrawButtonFlags::Pressed )
3164 GetButtonState() &= ~DrawButtonFlags::Pressed;
3165 Invalidate();
3171 void CheckBox::KeyInput( const KeyEvent& rKEvt )
3173 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3175 if ( !aKeyCode.GetModifier() && (aKeyCode.GetCode() == KEY_SPACE) )
3177 if ( !(GetButtonState() & DrawButtonFlags::Pressed) )
3179 GetButtonState() |= DrawButtonFlags::Pressed;
3180 Invalidate();
3183 else if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_ESCAPE) )
3185 GetButtonState() &= ~DrawButtonFlags::Pressed;
3186 Invalidate();
3188 else
3189 Button::KeyInput( rKEvt );
3192 void CheckBox::KeyUp( const KeyEvent& rKEvt )
3194 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
3196 if ( (GetButtonState() & DrawButtonFlags::Pressed) && (aKeyCode.GetCode() == KEY_SPACE) )
3198 GetButtonState() &= ~DrawButtonFlags::Pressed;
3199 ImplCheck();
3201 else
3202 Button::KeyUp( rKEvt );
3205 void CheckBox::FillLayoutData() const
3207 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
3208 const_cast<CheckBox*>(this)->Invalidate();
3211 void CheckBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
3213 ImplDrawCheckBox(rRenderContext);
3216 void CheckBox::Draw( OutputDevice* pDev, const Point& rPos,
3217 DrawFlags nFlags )
3219 MapMode aResMapMode( MapUnit::Map100thMM );
3220 Point aPos = pDev->LogicToPixel( rPos );
3221 Size aSize = GetSizePixel();
3222 Size aImageSize = pDev->LogicToPixel( Size( 300, 300 ), aResMapMode );
3223 Size aBrd1Size = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode );
3224 Size aBrd2Size = pDev->LogicToPixel( Size( 30, 30 ), aResMapMode );
3225 tools::Long nCheckWidth = pDev->LogicToPixel( Size( 20, 20 ), aResMapMode ).Width();
3226 vcl::Font aFont = GetDrawPixelFont( pDev );
3227 tools::Rectangle aStateRect;
3228 tools::Rectangle aMouseRect;
3230 aImageSize.setWidth( CalcZoom( aImageSize.Width() ) );
3231 aImageSize.setHeight( CalcZoom( aImageSize.Height() ) );
3232 aBrd1Size.setWidth( CalcZoom( aBrd1Size.Width() ) );
3233 aBrd1Size.setHeight( CalcZoom( aBrd1Size.Height() ) );
3234 aBrd2Size.setWidth( CalcZoom( aBrd2Size.Width() ) );
3235 aBrd2Size.setHeight( CalcZoom( aBrd2Size.Height() ) );
3237 if ( !aBrd1Size.Width() )
3238 aBrd1Size.setWidth( 1 );
3239 if ( !aBrd1Size.Height() )
3240 aBrd1Size.setHeight( 1 );
3241 if ( !aBrd2Size.Width() )
3242 aBrd2Size.setWidth( 1 );
3243 if ( !aBrd2Size.Height() )
3244 aBrd2Size.setHeight( 1 );
3245 if ( !nCheckWidth )
3246 nCheckWidth = 1;
3248 pDev->Push();
3249 pDev->SetMapMode();
3250 pDev->SetFont( aFont );
3251 if ( nFlags & DrawFlags::Mono )
3252 pDev->SetTextColor( COL_BLACK );
3253 else
3254 pDev->SetTextColor( GetTextColor() );
3255 pDev->SetTextFillColor();
3257 ImplDraw( pDev, nFlags, aPos, aSize,
3258 aImageSize, aStateRect, aMouseRect );
3260 pDev->SetLineColor();
3261 pDev->SetFillColor( COL_BLACK );
3262 pDev->DrawRect( aStateRect );
3263 aStateRect.AdjustLeft(aBrd1Size.Width() );
3264 aStateRect.AdjustTop(aBrd1Size.Height() );
3265 aStateRect.AdjustRight( -(aBrd1Size.Width()) );
3266 aStateRect.AdjustBottom( -(aBrd1Size.Height()) );
3267 if ( meState == TRISTATE_INDET )
3268 pDev->SetFillColor( COL_LIGHTGRAY );
3269 else
3270 pDev->SetFillColor( COL_WHITE );
3271 pDev->DrawRect( aStateRect );
3273 if ( meState == TRISTATE_TRUE )
3275 aStateRect.AdjustLeft(aBrd2Size.Width() );
3276 aStateRect.AdjustTop(aBrd2Size.Height() );
3277 aStateRect.AdjustRight( -(aBrd2Size.Width()) );
3278 aStateRect.AdjustBottom( -(aBrd2Size.Height()) );
3279 Point aPos11( aStateRect.TopLeft() );
3280 Point aPos12( aStateRect.BottomRight() );
3281 Point aPos21( aStateRect.TopRight() );
3282 Point aPos22( aStateRect.BottomLeft() );
3283 Point aTempPos11( aPos11 );
3284 Point aTempPos12( aPos12 );
3285 Point aTempPos21( aPos21 );
3286 Point aTempPos22( aPos22 );
3287 pDev->SetLineColor( COL_BLACK );
3288 tools::Long nDX = 0;
3289 for ( tools::Long i = 0; i < nCheckWidth; i++ )
3291 if ( !(i % 2) )
3293 aTempPos11.setX( aPos11.X()+nDX );
3294 aTempPos12.setX( aPos12.X()+nDX );
3295 aTempPos21.setX( aPos21.X()+nDX );
3296 aTempPos22.setX( aPos22.X()+nDX );
3298 else
3300 nDX++;
3301 aTempPos11.setX( aPos11.X()-nDX );
3302 aTempPos12.setX( aPos12.X()-nDX );
3303 aTempPos21.setX( aPos21.X()-nDX );
3304 aTempPos22.setX( aPos22.X()-nDX );
3306 pDev->DrawLine( aTempPos11, aTempPos12 );
3307 pDev->DrawLine( aTempPos21, aTempPos22 );
3311 pDev->Pop();
3314 void CheckBox::Resize()
3316 Control::Resize();
3317 Invalidate();
3320 void CheckBox::GetFocus()
3322 if (GetText().isEmpty())
3324 // increase button size to have space for focus rect
3325 // checkboxes without text will draw focusrect around the check
3326 // See CheckBox::ImplDraw()
3327 Point aPos( GetPosPixel() );
3328 Size aSize( GetSizePixel() );
3329 aPos.Move(-1,-1);
3330 aSize.AdjustHeight(2 );
3331 aSize.AdjustWidth(2 );
3332 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
3333 Invalidate();
3334 // Trigger drawing to initialize the mouse rectangle, otherwise the mouse button down
3335 // handler would ignore the mouse event.
3336 PaintImmediately();
3338 else
3339 ShowFocus( ImplGetFocusRect() );
3341 SetInputContext( InputContext( GetFont() ) );
3342 Button::GetFocus();
3345 void CheckBox::LoseFocus()
3347 if ( GetButtonState() & DrawButtonFlags::Pressed )
3349 GetButtonState() &= ~DrawButtonFlags::Pressed;
3350 Invalidate();
3353 HideFocus();
3354 Button::LoseFocus();
3356 if (GetText().isEmpty())
3358 // decrease button size again (see GetFocus())
3359 // checkboxes without text will draw focusrect around the check
3360 Point aPos( GetPosPixel() );
3361 Size aSize( GetSizePixel() );
3362 aPos.Move(1,1);
3363 aSize.AdjustHeight( -2 );
3364 aSize.AdjustWidth( -2 );
3365 setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
3366 Invalidate();
3370 void CheckBox::StateChanged( StateChangedType nType )
3372 Button::StateChanged( nType );
3374 if ( nType == StateChangedType::State )
3376 if ( IsReallyVisible() && IsUpdateMode() )
3377 Invalidate( maStateRect );
3379 else if ( (nType == StateChangedType::Enable) ||
3380 (nType == StateChangedType::Text) ||
3381 (nType == StateChangedType::Data) ||
3382 (nType == StateChangedType::UpdateMode) )
3384 if ( IsUpdateMode() )
3385 Invalidate();
3387 else if ( nType == StateChangedType::Style )
3389 SetStyle( ImplInitStyle( GetWindow( GetWindowType::Prev ), GetStyle() ) );
3391 if ( (GetPrevStyle() & CHECKBOX_VIEW_STYLE) !=
3392 (GetStyle() & CHECKBOX_VIEW_STYLE) )
3394 if ( IsUpdateMode() )
3395 Invalidate();
3398 else if ( (nType == StateChangedType::Zoom) ||
3399 (nType == StateChangedType::ControlFont) )
3401 ImplInitSettings( false );
3402 Invalidate();
3404 else if ( nType == StateChangedType::ControlForeground )
3406 ImplInitSettings( false );
3407 Invalidate();
3409 else if ( nType == StateChangedType::ControlBackground )
3411 ImplInitSettings( true );
3412 Invalidate();
3416 void CheckBox::DataChanged( const DataChangedEvent& rDCEvt )
3418 Button::DataChanged( rDCEvt );
3420 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
3421 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
3422 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
3423 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
3425 ImplInitSettings( true );
3426 Invalidate();
3430 bool CheckBox::PreNotify( NotifyEvent& rNEvt )
3432 if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
3434 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
3435 if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
3437 // trigger redraw if mouse over state has changed
3438 if( IsNativeControlSupported(ControlType::Checkbox, ControlPart::Entire) )
3440 if (maMouseRect.IsInside(GetPointerPosPixel()) != maMouseRect.IsInside(GetLastPointerPosPixel()) ||
3441 pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
3443 Invalidate( maStateRect );
3449 return Button::PreNotify(rNEvt);
3452 void CheckBox::Toggle()
3454 ImplCallEventListenersAndHandler( VclEventId::CheckboxToggle, [this] () { maToggleHdl.Call(*this); } );
3457 void CheckBox::SetState( TriState eState )
3459 if ( !mbTriState && (eState == TRISTATE_INDET) )
3460 eState = TRISTATE_FALSE;
3462 if ( meState != eState )
3464 meState = eState;
3465 StateChanged( StateChangedType::State );
3466 Toggle();
3470 bool CheckBox::set_property(const OString &rKey, const OUString &rValue)
3472 if (rKey == "active")
3473 SetState(toBool(rValue) ? TRISTATE_TRUE : TRISTATE_FALSE);
3474 else
3475 return Button::set_property(rKey, rValue);
3476 return true;
3479 void CheckBox::EnableTriState( bool bTriState )
3481 if ( mbTriState != bTriState )
3483 mbTriState = bTriState;
3485 if ( !bTriState && (meState == TRISTATE_INDET) )
3486 SetState( TRISTATE_FALSE );
3490 tools::Long CheckBox::ImplGetImageToTextDistance() const
3492 // 4 pixels, but take zoom into account, so the text doesn't "jump" relative to surrounding elements,
3493 // which might have been aligned with the text of the check box
3494 return CalcZoom( 4 );
3497 Size CheckBox::ImplGetCheckImageSize() const
3499 Size aSize;
3500 bool bDefaultSize = true;
3501 if( IsNativeControlSupported( ControlType::Checkbox, ControlPart::Entire ) )
3503 ImplControlValue aControlValue;
3504 tools::Rectangle aCtrlRegion( Point( 0, 0 ), GetSizePixel() );
3505 tools::Rectangle aBoundingRgn, aContentRgn;
3507 // get native size of a check box
3508 if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3509 ControlState::DEFAULT|ControlState::ENABLED,
3510 aControlValue,
3511 aBoundingRgn, aContentRgn ) )
3513 aSize = aContentRgn.GetSize();
3514 bDefaultSize = false;
3517 if( bDefaultSize )
3518 aSize = GetCheckImage( GetSettings(), DrawButtonFlags::NONE ).GetSizePixel();
3519 return aSize;
3522 Image CheckBox::GetCheckImage( const AllSettings& rSettings, DrawButtonFlags nFlags )
3524 ImplSVData* pSVData = ImplGetSVData();
3525 const StyleSettings& rStyleSettings = rSettings.GetStyleSettings();
3526 sal_uInt16 nStyle = 0;
3528 if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
3529 nStyle = STYLE_CHECKBOX_MONO;
3531 if ( pSVData->maCtrlData.maCheckImgList.empty() ||
3532 (pSVData->maCtrlData.mnCheckStyle != nStyle) ||
3533 (pSVData->maCtrlData.mnLastCheckFColor != rStyleSettings.GetFaceColor()) ||
3534 (pSVData->maCtrlData.mnLastCheckWColor != rStyleSettings.GetWindowColor()) ||
3535 (pSVData->maCtrlData.mnLastCheckLColor != rStyleSettings.GetLightColor()) )
3537 pSVData->maCtrlData.maCheckImgList.clear();
3539 pSVData->maCtrlData.mnLastCheckFColor = rStyleSettings.GetFaceColor();
3540 pSVData->maCtrlData.mnLastCheckWColor = rStyleSettings.GetWindowColor();
3541 pSVData->maCtrlData.mnLastCheckLColor = rStyleSettings.GetLightColor();
3543 std::vector<OUString> aResources;
3544 if (nStyle)
3546 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO1);
3547 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO2);
3548 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO3);
3549 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO4);
3550 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO5);
3551 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO6);
3552 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO7);
3553 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO8);
3554 aResources.emplace_back(SV_RESID_BITMAP_CHECKMONO9);
3556 else
3558 aResources.emplace_back(SV_RESID_BITMAP_CHECK1);
3559 aResources.emplace_back(SV_RESID_BITMAP_CHECK2);
3560 aResources.emplace_back(SV_RESID_BITMAP_CHECK3);
3561 aResources.emplace_back(SV_RESID_BITMAP_CHECK4);
3562 aResources.emplace_back(SV_RESID_BITMAP_CHECK5);
3563 aResources.emplace_back(SV_RESID_BITMAP_CHECK6);
3564 aResources.emplace_back(SV_RESID_BITMAP_CHECK7);
3565 aResources.emplace_back(SV_RESID_BITMAP_CHECK8);
3566 aResources.emplace_back(SV_RESID_BITMAP_CHECK9);
3568 LoadThemedImageList(rStyleSettings, pSVData->maCtrlData.maCheckImgList, aResources);
3569 pSVData->maCtrlData.mnCheckStyle = nStyle;
3572 sal_uInt16 nIndex;
3573 if ( nFlags & DrawButtonFlags::Disabled )
3575 if ( nFlags & DrawButtonFlags::DontKnow )
3576 nIndex = 8;
3577 else if ( nFlags & DrawButtonFlags::Checked )
3578 nIndex = 5;
3579 else
3580 nIndex = 4;
3582 else if ( nFlags & DrawButtonFlags::Pressed )
3584 if ( nFlags & DrawButtonFlags::DontKnow )
3585 nIndex = 7;
3586 else if ( nFlags & DrawButtonFlags::Checked )
3587 nIndex = 3;
3588 else
3589 nIndex = 2;
3591 else
3593 if ( nFlags & DrawButtonFlags::DontKnow )
3594 nIndex = 6;
3595 else if ( nFlags & DrawButtonFlags::Checked )
3596 nIndex = 1;
3597 else
3598 nIndex = 0;
3600 return pSVData->maCtrlData.maCheckImgList[nIndex];
3603 void CheckBox::ImplAdjustNWFSizes()
3605 Push( PushFlags::MAPMODE );
3606 SetMapMode(MapMode(MapUnit::MapPixel));
3608 ImplControlValue aControlValue;
3609 Size aCurSize( GetSizePixel() );
3610 tools::Rectangle aCtrlRegion( Point( 0, 0 ), aCurSize );
3611 tools::Rectangle aBoundingRgn, aContentRgn;
3613 // get native size of a radiobutton
3614 if( GetNativeControlRegion( ControlType::Checkbox, ControlPart::Entire, aCtrlRegion,
3615 ControlState::DEFAULT|ControlState::ENABLED, aControlValue,
3616 aBoundingRgn, aContentRgn ) )
3618 Size aSize = aContentRgn.GetSize();
3620 if( aSize.Height() > aCurSize.Height() )
3622 aCurSize.setHeight( aSize.Height() );
3623 SetSizePixel( aCurSize );
3627 Pop();
3630 Size CheckBox::CalcMinimumSize( tools::Long nMaxWidth ) const
3632 Size aSize = ImplGetCheckImageSize();
3633 nMaxWidth -= aSize.Width();
3635 OUString aText = GetText();
3636 if (!aText.isEmpty())
3638 // subtract what will be added later
3639 nMaxWidth-=2;
3640 nMaxWidth -= ImplGetImageToTextDistance();
3642 Size aTextSize = GetTextRect( tools::Rectangle( Point(), Size( nMaxWidth > 0 ? nMaxWidth : 0x7fffffff, 0x7fffffff ) ),
3643 aText, FixedText::ImplGetTextStyle( GetStyle() ) ).GetSize();
3644 aSize.AdjustWidth(2 ); // for focus rect
3645 aSize.AdjustWidth(ImplGetImageToTextDistance() );
3646 aSize.AdjustWidth(aTextSize.Width() );
3647 if ( aSize.Height() < aTextSize.Height() )
3648 aSize.setHeight( aTextSize.Height() );
3650 else
3652 // is this still correct ? since the checkbox now
3653 // shows a focus rect it should be 2 pixels wider and longer
3654 /* since otherwise the controls in the Writer hang too far up
3655 aSize.Width() += 2;
3656 aSize.Height() += 2;
3660 return CalcWindowSize( aSize );
3663 Size CheckBox::GetOptimalSize() const
3665 int nWidthRequest(get_width_request());
3666 return CalcMinimumSize(nWidthRequest != -1 ? nWidthRequest : 0);
3669 void CheckBox::ShowFocus(const tools::Rectangle& rRect)
3671 if (IsNativeControlSupported(ControlType::Checkbox, ControlPart::Focus))
3673 ImplControlValue aControlValue;
3674 tools::Rectangle aInRect(Point(0, 0), GetSizePixel());
3676 aInRect.SetLeft( rRect.Left() ); // exclude the checkbox itself from the focusrect
3678 DrawNativeControl(ControlType::Checkbox, ControlPart::Focus, aInRect,
3679 ControlState::FOCUSED, aControlValue, OUString());
3681 Button::ShowFocus(rRect);
3684 void CheckBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
3686 Button::DumpAsPropertyTree(rJsonWriter);
3687 rJsonWriter.put("checked", IsChecked());
3690 FactoryFunction CheckBox::GetUITestFactory() const
3692 return CheckBoxUIObject::create;
3695 ImageButton::ImageButton( vcl::Window* pParent, WinBits nStyle ) :
3696 PushButton( pParent, nStyle )
3698 ImplInitStyle();
3701 void ImageButton::ImplInitStyle()
3703 WinBits nStyle = GetStyle();
3705 if ( ! ( nStyle & ( WB_RIGHT | WB_LEFT ) ) )
3706 nStyle |= WB_CENTER;
3708 if ( ! ( nStyle & ( WB_TOP | WB_BOTTOM ) ) )
3709 nStyle |= WB_VCENTER;
3711 SetStyle( nStyle );
3714 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */