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