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