Update git submodules
[LibreOffice.git] / vcl / source / control / spinfld.cxx
blob973825a3a977c6500b1fb6375d180754c287244d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/commandevent.hxx>
21 #include <vcl/event.hxx>
22 #include <vcl/decoview.hxx>
23 #include <vcl/toolkit/spinfld.hxx>
24 #include <vcl/settings.hxx>
25 #include <vcl/uitest/uiobject.hxx>
26 #include <sal/log.hxx>
28 #include <spin.hxx>
29 #include <svdata.hxx>
31 namespace {
33 void ImplGetSpinbuttonValue(vcl::Window* pWin,
34 const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
35 bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
36 bool bHorz, SpinbuttonValue& rValue )
38 // convert spinbutton data to a SpinbuttonValue structure for native painting
40 rValue.maUpperRect = rUpperRect;
41 rValue.maLowerRect = rLowerRect;
43 Point aPointerPos = pWin->GetPointerPosPixel();
45 ControlState nState = ControlState::ENABLED;
46 if (bUpperIn)
47 nState |= ControlState::PRESSED;
48 if (!pWin->IsEnabled() || !bUpperEnabled)
49 nState &= ~ControlState::ENABLED;
50 if (pWin->HasFocus())
51 nState |= ControlState::FOCUSED;
52 if (pWin->IsMouseOver() && rUpperRect.Contains(aPointerPos))
53 nState |= ControlState::ROLLOVER;
54 rValue.mnUpperState = nState;
56 nState = ControlState::ENABLED;
57 if (bLowerIn)
58 nState |= ControlState::PRESSED;
59 if (!pWin->IsEnabled() || !bLowerEnabled)
60 nState &= ~ControlState::ENABLED;
61 if (pWin->HasFocus())
62 nState |= ControlState::FOCUSED;
63 // for overlapping spins: highlight only one
64 if (pWin->IsMouseOver() && rLowerRect.Contains(aPointerPos) && !rUpperRect.Contains(aPointerPos))
65 nState |= ControlState::ROLLOVER;
66 rValue.mnLowerState = nState;
68 rValue.mnUpperPart = bHorz ? ControlPart::ButtonLeft : ControlPart::ButtonUp;
69 rValue.mnLowerPart = bHorz ? ControlPart::ButtonRight : ControlPart::ButtonDown;
72 bool ImplDrawNativeSpinfield(vcl::RenderContext& rRenderContext, vcl::Window const * pWin, const SpinbuttonValue& rSpinbuttonValue)
74 bool bNativeOK = false;
76 if (rRenderContext.IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) &&
77 // there is just no useful native support for spinfields with dropdown
78 !(pWin->GetStyle() & WB_DROPDOWN))
80 if (rRenderContext.IsNativeControlSupported(ControlType::Spinbox, rSpinbuttonValue.mnUpperPart) &&
81 rRenderContext.IsNativeControlSupported(ControlType::Spinbox, rSpinbuttonValue.mnLowerPart))
83 // only paint the embedded spin buttons, all buttons are painted at once
84 tools::Rectangle aUpperAndLowerButtons( rSpinbuttonValue.maUpperRect.GetUnion( rSpinbuttonValue.maLowerRect ) );
85 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Spinbox, ControlPart::AllButtons, aUpperAndLowerButtons,
86 ControlState::ENABLED, rSpinbuttonValue, OUString());
88 else
90 // paint the spinbox as a whole, use borderwindow to have proper clipping
91 vcl::Window* pBorder = pWin->GetWindow(GetWindowType::Border);
93 // to not overwrite everything, set the button region as clipregion to the border window
94 tools::Rectangle aClipRect(rSpinbuttonValue.maLowerRect);
95 aClipRect.Union(rSpinbuttonValue.maUpperRect);
97 vcl::RenderContext* pContext = &rRenderContext;
98 vcl::Region oldRgn;
99 Point aPt;
100 Size aSize(pBorder->GetOutputSizePixel()); // the size of the border window, i.e., the whole control
101 tools::Rectangle aNatRgn(aPt, aSize);
103 if (!pWin->SupportsDoubleBuffering())
105 // convert from screen space to borderwin space
106 aClipRect.SetPos(pBorder->ScreenToOutputPixel(pWin->OutputToScreenPixel(aClipRect.TopLeft())));
108 oldRgn = pBorder->GetOutDev()->GetClipRegion();
109 pBorder->GetOutDev()->SetClipRegion(vcl::Region(aClipRect));
111 pContext = pBorder->GetOutDev();
114 tools::Rectangle aBound, aContent;
115 if (!ImplGetSVData()->maNWFData.mbCanDrawWidgetAnySize &&
116 pContext->GetNativeControlRegion(ControlType::Spinbox, ControlPart::Entire,
117 aNatRgn, ControlState::NONE, rSpinbuttonValue,
118 aBound, aContent))
120 aSize = aContent.GetSize();
123 tools::Rectangle aRgn(aPt, aSize);
124 if (pWin->SupportsDoubleBuffering())
126 // convert from borderwin space, to the pWin's space
127 aRgn.SetPos(pWin->ScreenToOutputPixel(pBorder->OutputToScreenPixel(aRgn.TopLeft())));
130 bNativeOK = pContext->DrawNativeControl(ControlType::Spinbox, ControlPart::Entire, aRgn,
131 ControlState::ENABLED, rSpinbuttonValue, OUString());
133 if (!pWin->SupportsDoubleBuffering())
134 pBorder->GetOutDev()->SetClipRegion(oldRgn);
137 return bNativeOK;
140 bool ImplDrawNativeSpinbuttons(vcl::RenderContext& rRenderContext, const SpinbuttonValue& rSpinbuttonValue)
142 bool bNativeOK = false;
144 if (rRenderContext.IsNativeControlSupported(ControlType::SpinButtons, ControlPart::Entire))
146 tools::Rectangle aArea = rSpinbuttonValue.maUpperRect.GetUnion(rSpinbuttonValue.maLowerRect);
147 // only paint the standalone spin buttons, all buttons are painted at once
148 bNativeOK = rRenderContext.DrawNativeControl(ControlType::SpinButtons, ControlPart::AllButtons, aArea,
149 ControlState::ENABLED, rSpinbuttonValue, OUString());
151 return bNativeOK;
156 void ImplDrawSpinButton(vcl::RenderContext& rRenderContext, vcl::Window* pWindow,
157 const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
158 bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
159 bool bHorz, bool bMirrorHorz)
161 bool bNativeOK = false;
163 if (pWindow)
165 // are we drawing standalone spin buttons or members of a spinfield ?
166 ControlType aControl = ControlType::SpinButtons;
167 switch (pWindow->GetType())
169 case WindowType::EDIT:
170 case WindowType::MULTILINEEDIT:
171 case WindowType::PATTERNFIELD:
172 case WindowType::METRICFIELD:
173 case WindowType::CURRENCYFIELD:
174 case WindowType::DATEFIELD:
175 case WindowType::TIMEFIELD:
176 case WindowType::SPINFIELD:
177 case WindowType::FORMATTEDFIELD:
178 aControl = ControlType::Spinbox;
179 break;
180 default:
181 aControl = ControlType::SpinButtons;
182 break;
185 SpinbuttonValue aValue;
186 ImplGetSpinbuttonValue(pWindow, rUpperRect, rLowerRect,
187 bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
188 bHorz, aValue);
190 if( aControl == ControlType::Spinbox )
191 bNativeOK = ImplDrawNativeSpinfield(rRenderContext, pWindow, aValue);
192 else if( aControl == ControlType::SpinButtons )
193 bNativeOK = ImplDrawNativeSpinbuttons(rRenderContext, aValue);
196 if (bNativeOK)
197 return;
199 ImplDrawUpDownButtons(rRenderContext,
200 rUpperRect, rLowerRect,
201 bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
202 bHorz, bMirrorHorz);
205 void ImplDrawUpDownButtons(vcl::RenderContext& rRenderContext,
206 const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
207 bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
208 bool bHorz, bool bMirrorHorz)
210 DecorationView aDecoView(&rRenderContext);
212 SymbolType eType1, eType2;
214 if ( bHorz )
216 eType1 = bMirrorHorz ? SymbolType::SPIN_RIGHT : SymbolType::SPIN_LEFT;
217 eType2 = bMirrorHorz ? SymbolType::SPIN_LEFT : SymbolType::SPIN_RIGHT;
219 else
221 eType1 = SymbolType::SPIN_UP;
222 eType2 = SymbolType::SPIN_DOWN;
225 DrawButtonFlags nStyle = DrawButtonFlags::NoLeftLightBorder;
226 // draw upper/left Button
227 if (bUpperIn)
228 nStyle |= DrawButtonFlags::Pressed;
230 tools::Rectangle aUpRect = aDecoView.DrawButton(rUpperRect, nStyle);
232 nStyle = DrawButtonFlags::NoLeftLightBorder;
233 // draw lower/right Button
234 if (bLowerIn)
235 nStyle |= DrawButtonFlags::Pressed;
237 tools::Rectangle aLowRect = aDecoView.DrawButton(rLowerRect, nStyle);
239 // make use of additional default edge
240 aUpRect.AdjustLeft( -1 );
241 aUpRect.AdjustTop( -1 );
242 aUpRect.AdjustRight( 1 );
243 aUpRect.AdjustBottom( 1 );
244 aLowRect.AdjustLeft( -1 );
245 aLowRect.AdjustTop( -1 );
246 aLowRect.AdjustRight( 1 );
247 aLowRect.AdjustBottom( 1 );
249 // draw into the edge, so that something is visible if the rectangle is too small
250 if (aUpRect.GetHeight() < 4)
252 aUpRect.AdjustRight( 1 );
253 aUpRect.AdjustBottom( 1 );
254 aLowRect.AdjustRight( 1 );
255 aLowRect.AdjustBottom( 1 );
258 // calculate Symbol size
259 tools::Long nTempSize1 = aUpRect.GetWidth();
260 tools::Long nTempSize2 = aLowRect.GetWidth();
261 if (std::abs( nTempSize1-nTempSize2 ) == 1)
263 if (nTempSize1 > nTempSize2)
264 aUpRect.AdjustLeft( 1 );
265 else
266 aLowRect.AdjustLeft( 1 );
268 nTempSize1 = aUpRect.GetHeight();
269 nTempSize2 = aLowRect.GetHeight();
270 if (std::abs(nTempSize1 - nTempSize2) == 1)
272 if (nTempSize1 > nTempSize2)
273 aUpRect.AdjustTop( 1 );
274 else
275 aLowRect.AdjustTop( 1 );
278 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
280 DrawSymbolFlags nSymStyle = DrawSymbolFlags::NONE;
281 if (!bUpperEnabled)
282 nSymStyle |= DrawSymbolFlags::Disable;
283 aDecoView.DrawSymbol(aUpRect, eType1, rStyleSettings.GetButtonTextColor(), nSymStyle);
285 nSymStyle = DrawSymbolFlags::NONE;
286 if (!bLowerEnabled)
287 nSymStyle |= DrawSymbolFlags::Disable;
288 aDecoView.DrawSymbol(aLowRect, eType2, rStyleSettings.GetButtonTextColor(), nSymStyle);
291 void SpinField::ImplInitSpinFieldData()
293 mpEdit.disposeAndClear();
294 mbSpin = false;
295 mbRepeat = false;
296 mbUpperIn = false;
297 mbLowerIn = false;
298 mbInitialUp = false;
299 mbInitialDown = false;
300 mbInDropDown = false;
301 mbUpperEnabled = true;
302 mbLowerEnabled = true;
305 void SpinField::ImplInit(vcl::Window* pParent, WinBits nWinStyle)
307 Edit::ImplInit( pParent, nWinStyle );
309 if (!(nWinStyle & (WB_SPIN | WB_DROPDOWN)))
310 return;
312 mbSpin = true;
314 // Some themes want external spin buttons, therefore the main
315 // spinfield should not overdraw the border between its encapsulated
316 // edit field and the spin buttons
317 if ((nWinStyle & WB_SPIN) && ImplUseNativeBorder(*GetOutDev(), nWinStyle))
319 SetBackground();
320 mpEdit.set(VclPtr<Edit>::Create(this, WB_NOBORDER));
321 mpEdit->SetBackground();
323 else
324 mpEdit.set(VclPtr<Edit>::Create(this, WB_NOBORDER));
326 mpEdit->EnableRTL(false);
327 mpEdit->SetPosPixel(Point());
328 mpEdit->Show();
330 SetSubEdit(mpEdit);
332 maRepeatTimer.SetInvokeHandler(LINK( this, SpinField, ImplTimeout));
333 maRepeatTimer.SetTimeout(MouseSettings::GetButtonStartRepeat());
334 if (nWinStyle & WB_REPEAT)
335 mbRepeat = true;
337 SetCompoundControl(true);
340 SpinField::SpinField(vcl::Window* pParent, WinBits nWinStyle, WindowType nType) :
341 Edit(nType), maRepeatTimer("SpinField maRepeatTimer")
343 ImplInitSpinFieldData();
344 ImplInit(pParent, nWinStyle);
347 SpinField::~SpinField()
349 disposeOnce();
352 void SpinField::dispose()
354 mpEdit.disposeAndClear();
356 Edit::dispose();
359 void SpinField::Up()
361 ImplCallEventListenersAndHandler( VclEventId::SpinfieldUp, [this] () { maUpHdlLink.Call(*this); } );
364 void SpinField::Down()
366 ImplCallEventListenersAndHandler( VclEventId::SpinfieldDown, [this] () { maDownHdlLink.Call(*this); } );
369 void SpinField::First()
371 ImplCallEventListenersAndHandler(VclEventId::SpinfieldFirst, nullptr);
374 void SpinField::Last()
376 ImplCallEventListenersAndHandler(VclEventId::SpinfieldLast, nullptr);
379 void SpinField::MouseButtonDown( const MouseEvent& rMEvt )
381 if (!HasFocus() && (!mpEdit || !mpEdit->HasFocus()))
383 GrabFocus();
386 if (!IsReadOnly())
388 if (maUpperRect.Contains(rMEvt.GetPosPixel()))
390 mbUpperIn = true;
391 mbInitialUp = true;
392 Invalidate(maUpperRect);
394 else if (maLowerRect.Contains(rMEvt.GetPosPixel()))
396 mbLowerIn = true;
397 mbInitialDown = true;
398 Invalidate(maLowerRect);
400 else if (maDropDownRect.Contains(rMEvt.GetPosPixel()))
402 // put DropDownButton to the right
403 mbInDropDown = ShowDropDown( !mbInDropDown );
404 Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
407 if (mbUpperIn || mbLowerIn)
409 CaptureMouse();
410 if (mbRepeat)
411 maRepeatTimer.Start();
412 return;
416 Edit::MouseButtonDown(rMEvt);
419 void SpinField::MouseButtonUp(const MouseEvent& rMEvt)
421 ReleaseMouse();
422 mbInitialUp = mbInitialDown = false;
423 maRepeatTimer.Stop();
424 maRepeatTimer.SetTimeout(MouseSettings::GetButtonStartRepeat());
426 if (mbUpperIn)
428 mbUpperIn = false;
429 Invalidate(maUpperRect);
430 Up();
432 else if (mbLowerIn)
434 mbLowerIn = false;
435 Invalidate(maLowerRect);
436 Down();
439 Edit::MouseButtonUp(rMEvt);
442 void SpinField::MouseMove(const MouseEvent& rMEvt)
444 if (rMEvt.IsLeft())
446 if (mbInitialUp)
448 bool bNewUpperIn = maUpperRect.Contains(rMEvt.GetPosPixel());
449 if (bNewUpperIn != mbUpperIn)
451 if (bNewUpperIn)
453 if (mbRepeat)
454 maRepeatTimer.Start();
456 else
457 maRepeatTimer.Stop();
459 mbUpperIn = bNewUpperIn;
460 Invalidate(maUpperRect);
463 else if (mbInitialDown)
465 bool bNewLowerIn = maLowerRect.Contains(rMEvt.GetPosPixel());
466 if (bNewLowerIn != mbLowerIn)
468 if (bNewLowerIn)
470 if (mbRepeat)
471 maRepeatTimer.Start();
473 else
474 maRepeatTimer.Stop();
476 mbLowerIn = bNewLowerIn;
477 Invalidate(maLowerRect);
482 Edit::MouseMove(rMEvt);
485 bool SpinField::EventNotify(NotifyEvent& rNEvt)
487 bool bDone = false;
488 if (rNEvt.GetType() == NotifyEventType::KEYINPUT)
490 const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
491 if (!IsReadOnly())
493 sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
494 switch (rKEvt.GetKeyCode().GetCode())
496 case KEY_UP:
498 if (!nMod)
500 Up();
501 bDone = true;
504 break;
505 case KEY_DOWN:
507 if (!nMod)
509 Down();
510 bDone = true;
512 else if ((nMod == KEY_MOD2) && !mbInDropDown && (GetStyle() & WB_DROPDOWN))
514 mbInDropDown = ShowDropDown(true);
515 Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
516 bDone = true;
519 break;
520 case KEY_PAGEUP:
522 if (!nMod)
524 Last();
525 bDone = true;
528 break;
529 case KEY_PAGEDOWN:
531 if (!nMod)
533 First();
534 bDone = true;
537 break;
542 if (rNEvt.GetType() == NotifyEventType::COMMAND)
544 if ((rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) && !IsReadOnly())
546 MouseWheelBehaviour nWheelBehavior(GetSettings().GetMouseSettings().GetWheelBehavior());
547 if (nWheelBehavior == MouseWheelBehaviour::ALWAYS
548 || (nWheelBehavior == MouseWheelBehaviour::FocusOnly && HasChildPathFocus()))
550 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
551 if (pData->GetMode() == CommandWheelMode::SCROLL)
553 if (pData->GetDelta() < 0)
554 Down();
555 else
556 Up();
557 bDone = true;
559 if (!HasChildPathFocus())
560 GrabFocus();
563 else
564 bDone = false; // don't eat this event, let the default handling happen (i.e. scroll the context)
568 return bDone || Edit::EventNotify(rNEvt);
571 void SpinField::FillLayoutData() const
573 if (mbSpin)
575 mxLayoutData.emplace();
576 AppendLayoutData(*GetSubEdit());
577 GetSubEdit()->SetLayoutDataParent(this);
579 else
580 Edit::FillLayoutData();
583 void SpinField::SetUpperEnabled(bool bEnabled)
585 if (mbUpperEnabled == bEnabled)
586 return;
588 mbUpperEnabled = bEnabled;
590 if (mbSpin)
591 Invalidate(maUpperRect);
594 void SpinField::SetLowerEnabled(bool bEnabled)
596 if (mbLowerEnabled == bEnabled)
597 return;
599 mbLowerEnabled = bEnabled;
601 if (mbSpin)
602 Invalidate(maLowerRect);
605 void SpinField::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
607 if (mbSpin)
609 bool bEnabled = IsEnabled();
610 bool bUpperEnabled = bEnabled && IsUpperEnabled();
611 bool bLowerEnabled = bEnabled && IsLowerEnabled();
612 ImplDrawSpinButton(rRenderContext, this, maUpperRect, maLowerRect,
613 mbUpperIn && bUpperEnabled, mbLowerIn && bLowerEnabled,
614 bUpperEnabled, bLowerEnabled);
617 if (GetStyle() & WB_DROPDOWN)
619 DecorationView aView(&rRenderContext);
621 DrawButtonFlags nStyle = DrawButtonFlags::NoLightBorder;
622 if (mbInDropDown)
623 nStyle |= DrawButtonFlags::Pressed;
624 tools::Rectangle aInnerRect = aView.DrawButton(maDropDownRect, nStyle);
626 DrawSymbolFlags nSymbolStyle = IsEnabled() ? DrawSymbolFlags::NONE : DrawSymbolFlags::Disable;
627 aView.DrawSymbol(aInnerRect, SymbolType::SPIN_DOWN, rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor(), nSymbolStyle);
630 Edit::Paint(rRenderContext, rRect);
633 void SpinField::ImplCalcButtonAreas(const OutputDevice* pDev, const Size& rOutSz, tools::Rectangle& rDDArea,
634 tools::Rectangle& rSpinUpArea, tools::Rectangle& rSpinDownArea)
636 const StyleSettings& rStyleSettings = pDev->GetSettings().GetStyleSettings();
638 Size aSize = rOutSz;
639 Size aDropDownSize;
641 if (GetStyle() & WB_DROPDOWN)
643 tools::Long nW = rStyleSettings.GetScrollBarSize();
644 nW = GetDrawPixel( pDev, nW );
645 aDropDownSize = Size( CalcZoom( nW ), aSize.Height() );
646 aSize.AdjustWidth( -(aDropDownSize.Width()) );
647 rDDArea = tools::Rectangle( Point( aSize.Width(), 0 ), aDropDownSize );
648 rDDArea.AdjustTop( -1 );
650 else
651 rDDArea.SetEmpty();
653 // calculate sizes according to the height
654 if (GetStyle() & WB_SPIN)
656 tools::Long nBottom1 = aSize.Height()/2;
657 tools::Long nBottom2 = aSize.Height()-1;
658 tools::Long nTop2 = nBottom1;
659 if ( !(aSize.Height() & 0x01) )
660 nBottom1--;
662 bool bNativeRegionOK = false;
663 tools::Rectangle aContentUp, aContentDown;
665 if ((pDev->GetOutDevType() == OUTDEV_WINDOW) &&
666 // there is just no useful native support for spinfields with dropdown
667 ! (GetStyle() & WB_DROPDOWN) &&
668 IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire))
670 vcl::Window *pWin = pDev->GetOwnerWindow();
671 vcl::Window *pBorder = pWin->GetWindow( GetWindowType::Border );
673 // get the system's spin button size
674 ImplControlValue aControlValue;
675 tools::Rectangle aBound;
676 Point aPoint;
678 // use the full extent of the control
679 tools::Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
681 bNativeRegionOK =
682 pWin->GetNativeControlRegion(ControlType::Spinbox, ControlPart::ButtonUp,
683 aArea, ControlState::NONE, aControlValue, aBound, aContentUp) &&
684 pWin->GetNativeControlRegion(ControlType::Spinbox, ControlPart::ButtonDown,
685 aArea, ControlState::NONE, aControlValue, aBound, aContentDown);
687 if (bNativeRegionOK)
689 // convert back from border space to local coordinates
690 aPoint = pBorder->ScreenToOutputPixel( pWin->OutputToScreenPixel( aPoint ) );
691 aContentUp.Move(-aPoint.X(), -aPoint.Y());
692 aContentDown.Move(-aPoint.X(), -aPoint.Y());
696 if (bNativeRegionOK)
698 rSpinUpArea = aContentUp;
699 rSpinDownArea = aContentDown;
701 else
703 aSize.AdjustWidth( -(CalcZoom( GetDrawPixel( pDev, rStyleSettings.GetSpinSize() ) )) );
705 rSpinUpArea = tools::Rectangle( aSize.Width(), 0, rOutSz.Width()-aDropDownSize.Width()-1, nBottom1 );
706 rSpinDownArea = tools::Rectangle( rSpinUpArea.Left(), nTop2, rSpinUpArea.Right(), nBottom2 );
709 else
711 rSpinUpArea.SetEmpty();
712 rSpinDownArea.SetEmpty();
716 void SpinField::Resize()
718 if (!mbSpin)
719 return;
721 Control::Resize();
722 Size aSize = GetOutputSizePixel();
723 bool bSubEditPositioned = false;
725 if (GetStyle() & (WB_SPIN | WB_DROPDOWN))
727 ImplCalcButtonAreas( GetOutDev(), aSize, maDropDownRect, maUpperRect, maLowerRect );
729 ImplControlValue aControlValue;
730 Point aPoint;
731 tools::Rectangle aContent, aBound;
733 // use the full extent of the control
734 vcl::Window *pBorder = GetWindow( GetWindowType::Border );
735 tools::Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
737 // adjust position and size of the edit field
738 if (GetNativeControlRegion(ControlType::Spinbox, ControlPart::SubEdit, aArea, ControlState::NONE,
739 aControlValue, aBound, aContent) &&
740 // there is just no useful native support for spinfields with dropdown
741 !(GetStyle() & WB_DROPDOWN))
743 // convert back from border space to local coordinates
744 aPoint = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aPoint));
745 aContent.Move(-aPoint.X(), -aPoint.Y());
747 // use the themes drop down size
748 mpEdit->SetPosPixel( aContent.TopLeft() );
749 bSubEditPositioned = true;
750 aSize = aContent.GetSize();
752 else
754 if (maUpperRect.IsEmpty())
756 SAL_WARN_IF( maDropDownRect.IsEmpty(), "vcl", "SpinField::Resize: SPIN && DROPDOWN, but all empty rects?" );
757 aSize.setWidth( maDropDownRect.Left() );
759 else
760 aSize.setWidth( maUpperRect.Left() );
764 if (!bSubEditPositioned)
766 // this moves our sub edit if RTL gets switched
767 mpEdit->SetPosPixel(Point());
769 mpEdit->SetSizePixel(aSize);
771 if (GetStyle() & WB_SPIN)
772 Invalidate(tools::Rectangle(maUpperRect.TopLeft(), maLowerRect.BottomRight()));
773 if (GetStyle() & WB_DROPDOWN)
774 Invalidate(maDropDownRect);
777 void SpinField::StateChanged(StateChangedType nType)
779 Edit::StateChanged(nType);
781 if (nType == StateChangedType::Enable)
783 if (mbSpin || (GetStyle() & WB_DROPDOWN))
785 mpEdit->Enable(IsEnabled());
787 if (mbSpin)
789 Invalidate(maLowerRect);
790 Invalidate(maUpperRect);
792 if (GetStyle() & WB_DROPDOWN)
793 Invalidate(maDropDownRect);
796 else if (nType == StateChangedType::Style)
798 if (GetStyle() & WB_REPEAT)
799 mbRepeat = true;
800 else
801 mbRepeat = false;
803 else if (nType == StateChangedType::Zoom)
805 Resize();
806 if (mpEdit)
807 mpEdit->SetZoom(GetZoom());
808 Invalidate();
810 else if (nType == StateChangedType::ControlFont)
812 if (mpEdit)
813 mpEdit->SetControlFont(GetControlFont());
814 Invalidate();
816 else if (nType == StateChangedType::ControlForeground)
818 if (mpEdit)
819 mpEdit->SetControlForeground(GetControlForeground());
820 Invalidate();
822 else if (nType == StateChangedType::ControlBackground)
824 if (mpEdit)
825 mpEdit->SetControlBackground(GetControlBackground());
826 Invalidate();
828 else if( nType == StateChangedType::Mirroring )
830 if (mpEdit)
831 mpEdit->CompatStateChanged(StateChangedType::Mirroring);
832 Resize();
836 void SpinField::DataChanged( const DataChangedEvent& rDCEvt )
838 Edit::DataChanged(rDCEvt);
840 if ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
841 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
843 Resize();
844 Invalidate();
848 tools::Rectangle* SpinField::ImplFindPartRect(const Point& rPt)
850 if (maUpperRect.Contains(rPt))
851 return &maUpperRect;
852 else if (maLowerRect.Contains(rPt))
853 return &maLowerRect;
854 else
855 return nullptr;
858 bool SpinField::PreNotify(NotifyEvent& rNEvt)
860 if (rNEvt.GetType() == NotifyEventType::MOUSEMOVE)
862 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
863 if (pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged())
865 // trigger redraw if mouse over state has changed
866 if( IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) ||
867 IsNativeControlSupported(ControlType::Spinbox, ControlPart::AllButtons) )
869 tools::Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
870 tools::Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
871 if( pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()) )
873 if (!IsNativeWidgetEnabled() ||
874 !IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
876 // paint directly
877 vcl::Region aRgn( GetOutDev()->GetActiveClipRegion() );
878 if (pLastRect)
880 GetOutDev()->SetClipRegion(vcl::Region(*pLastRect));
881 Invalidate(*pLastRect);
882 GetOutDev()->SetClipRegion( aRgn );
884 if (pRect)
886 GetOutDev()->SetClipRegion(vcl::Region(*pRect));
887 Invalidate(*pRect);
888 GetOutDev()->SetClipRegion( aRgn );
896 return Edit::PreNotify(rNEvt);
899 void SpinField::EndDropDown()
901 mbInDropDown = false;
902 Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
905 bool SpinField::ShowDropDown( bool )
907 return false;
910 Size SpinField::CalcMinimumSizeForText(const OUString &rString) const
912 Size aSz = Edit::CalcMinimumSizeForText(rString);
914 if ( GetStyle() & WB_DROPDOWN )
915 aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
916 if ( GetStyle() & WB_SPIN )
918 ImplControlValue aControlValue;
919 tools::Rectangle aArea( Point(), Size(100, aSz.Height()));
920 tools::Rectangle aEntireBound, aEntireContent, aEditBound, aEditContent;
921 if (
922 GetNativeControlRegion(ControlType::Spinbox, ControlPart::Entire,
923 aArea, ControlState::NONE, aControlValue, aEntireBound, aEntireContent) &&
924 GetNativeControlRegion(ControlType::Spinbox, ControlPart::SubEdit,
925 aArea, ControlState::NONE, aControlValue, aEditBound, aEditContent)
928 aSz.AdjustWidth(aEntireContent.GetWidth() - aEditContent.GetWidth());
930 else
932 aSz.AdjustWidth(maUpperRect.GetWidth() );
936 return aSz;
939 Size SpinField::CalcMinimumSize() const
941 return CalcMinimumSizeForText(GetText());
944 Size SpinField::GetOptimalSize() const
946 return CalcMinimumSize();
949 Size SpinField::CalcSize(sal_Int32 nChars) const
951 Size aSz = Edit::CalcSize( nChars );
953 if ( GetStyle() & WB_DROPDOWN )
954 aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
955 if ( GetStyle() & WB_SPIN )
956 aSz.AdjustWidth(GetSettings().GetStyleSettings().GetSpinSize() );
958 return aSz;
961 IMPL_LINK( SpinField, ImplTimeout, Timer*, pTimer, void )
963 if ( pTimer->GetTimeout() == static_cast<sal_uInt64>(MouseSettings::GetButtonStartRepeat()) )
965 pTimer->SetTimeout( GetSettings().GetMouseSettings().GetButtonRepeat() );
966 pTimer->Start();
968 else
970 if ( mbInitialUp )
971 Up();
972 else
973 Down();
977 void SpinField::Draw(OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags)
979 Edit::Draw(pDev, rPos, nFlags);
981 WinBits nFieldStyle = GetStyle();
982 if ( (nFlags & SystemTextColorFlags::NoControls ) || !( nFieldStyle & (WB_SPIN|WB_DROPDOWN) ) )
983 return;
985 Point aPos = pDev->LogicToPixel( rPos );
986 Size aSize = GetSizePixel();
987 AllSettings aOldSettings = pDev->GetSettings();
989 pDev->Push();
990 pDev->SetMapMode();
992 tools::Rectangle aDD, aUp, aDown;
993 ImplCalcButtonAreas(pDev, aSize, aDD, aUp, aDown);
994 aDD.Move(aPos.X(), aPos.Y());
995 aUp.Move(aPos.X(), aPos.Y());
996 aUp.AdjustTop( 1 );
997 aDown.Move(aPos.X(), aPos.Y());
999 Color aButtonTextColor;
1000 if (nFlags & SystemTextColorFlags::Mono)
1001 aButtonTextColor = COL_BLACK;
1002 else
1003 aButtonTextColor = GetSettings().GetStyleSettings().GetButtonTextColor();
1005 if (GetStyle() & WB_DROPDOWN)
1007 DecorationView aView( pDev );
1008 tools::Rectangle aInnerRect = aView.DrawButton( aDD, DrawButtonFlags::NoLightBorder );
1009 DrawSymbolFlags nSymbolStyle = IsEnabled() ? DrawSymbolFlags::NONE : DrawSymbolFlags::Disable;
1010 aView.DrawSymbol(aInnerRect, SymbolType::SPIN_DOWN, aButtonTextColor, nSymbolStyle);
1013 if (GetStyle() & WB_SPIN)
1015 ImplDrawSpinButton(*pDev, this, aUp, aDown, false, false);
1018 pDev->Pop();
1019 pDev->SetSettings(aOldSettings);
1023 FactoryFunction SpinField::GetUITestFactory() const
1025 return SpinFieldUIObject::create;
1028 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */