build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / control / spinfld.cxx
blobb5f659e5be109dfb98478698ed8276c8f73d4173
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "tools/rc.h"
22 #include <vcl/event.hxx>
23 #include <vcl/decoview.hxx>
24 #include <vcl/spinfld.hxx>
25 #include <vcl/settings.hxx>
26 #include <vcl/uitest/uiobject.hxx>
28 #include "controldata.hxx"
29 #include "spin.hxx"
30 #include "svdata.hxx"
32 namespace {
34 void ImplGetSpinbuttonValue(vcl::Window* pWin,
35 const Rectangle& rUpperRect, const Rectangle& rLowerRect,
36 bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
37 bool bHorz, SpinbuttonValue& rValue )
39 // convert spinbutton data to a SpinbuttonValue structure for native painting
41 rValue.maUpperRect = rUpperRect;
42 rValue.maLowerRect = rLowerRect;
44 Point aPointerPos = pWin->GetPointerPosPixel();
46 ControlState nState = ControlState::ENABLED;
47 if (bUpperIn)
48 nState |= ControlState::PRESSED;
49 if (!pWin->IsEnabled() || !bUpperEnabled)
50 nState &= ~ControlState::ENABLED;
51 if (pWin->HasFocus())
52 nState |= ControlState::FOCUSED;
53 if (pWin->IsMouseOver() && rUpperRect.IsInside(aPointerPos))
54 nState |= ControlState::ROLLOVER;
55 rValue.mnUpperState = nState;
57 nState = ControlState::ENABLED;
58 if (bLowerIn)
59 nState |= ControlState::PRESSED;
60 if (!pWin->IsEnabled() || !bLowerEnabled)
61 nState &= ~ControlState::ENABLED;
62 if (pWin->HasFocus())
63 nState |= ControlState::FOCUSED;
64 // for overlapping spins: highlight only one
65 if (pWin->IsMouseOver() && rLowerRect.IsInside(aPointerPos) && !rUpperRect.IsInside(aPointerPos))
66 nState |= ControlState::ROLLOVER;
67 rValue.mnLowerState = nState;
69 rValue.mnUpperPart = bHorz ? ControlPart::ButtonLeft : ControlPart::ButtonUp;
70 rValue.mnLowerPart = bHorz ? ControlPart::ButtonRight : ControlPart::ButtonDown;
73 bool ImplDrawNativeSpinfield(vcl::RenderContext& rRenderContext, vcl::Window* pWin, const SpinbuttonValue& rSpinbuttonValue)
75 bool bNativeOK = false;
77 if (rRenderContext.IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) &&
78 // there is just no useful native support for spinfields with dropdown
79 !(pWin->GetStyle() & WB_DROPDOWN))
81 if (rRenderContext.IsNativeControlSupported(ControlType::Spinbox, rSpinbuttonValue.mnUpperPart) &&
82 rRenderContext.IsNativeControlSupported(ControlType::Spinbox, rSpinbuttonValue.mnLowerPart))
84 // only paint the embedded spin buttons, all buttons are painted at once
85 Rectangle aUpperAndLowerButtons( rSpinbuttonValue.maUpperRect.GetUnion( rSpinbuttonValue.maLowerRect ) );
86 bNativeOK = rRenderContext.DrawNativeControl(ControlType::Spinbox, ControlPart::AllButtons, aUpperAndLowerButtons,
87 ControlState::ENABLED, rSpinbuttonValue, OUString());
89 else
91 // paint the spinbox as a whole, use borderwindow to have proper clipping
92 vcl::Window* pBorder = pWin->GetWindow(GetWindowType::Border);
94 // to not overwrite everything, set the button region as clipregion to the border window
95 Rectangle aClipRect(rSpinbuttonValue.maLowerRect);
96 aClipRect.Union(rSpinbuttonValue.maUpperRect);
98 vcl::RenderContext* pContext = &rRenderContext;
99 vcl::Region oldRgn;
100 Point aPt;
101 Size aSize(pBorder->GetOutputSizePixel()); // the size of the border window, i.e., the whole control
102 Rectangle aNatRgn(aPt, aSize);
104 if (!pWin->SupportsDoubleBuffering())
106 // convert from screen space to borderwin space
107 aClipRect.SetPos(pBorder->ScreenToOutputPixel(pWin->OutputToScreenPixel(aClipRect.TopLeft())));
109 oldRgn = pBorder->GetClipRegion();
110 pBorder->SetClipRegion(vcl::Region(aClipRect));
112 pContext = pBorder;
115 Rectangle aBound, aContent;
116 if (!ImplGetSVData()->maNWFData.mbCanDrawWidgetAnySize &&
117 pContext->GetNativeControlRegion(ControlType::Spinbox, ControlPart::Entire,
118 aNatRgn, ControlState::NONE, rSpinbuttonValue,
119 OUString(), aBound, aContent))
121 aSize = aContent.GetSize();
124 Rectangle aRgn(aPt, aSize);
125 if (pWin->SupportsDoubleBuffering())
127 // convert from borderwin space, to the pWin's space
128 aRgn.SetPos(pWin->ScreenToOutputPixel(pBorder->OutputToScreenPixel(aRgn.TopLeft())));
131 bNativeOK = pContext->DrawNativeControl(ControlType::Spinbox, ControlPart::Entire, aRgn,
132 ControlState::ENABLED, rSpinbuttonValue, OUString());
134 if (!pWin->SupportsDoubleBuffering())
135 pBorder->SetClipRegion(vcl::Region(oldRgn));
138 return bNativeOK;
141 bool ImplDrawNativeSpinbuttons(vcl::RenderContext& rRenderContext, const SpinbuttonValue& rSpinbuttonValue)
143 bool bNativeOK = false;
145 if (rRenderContext.IsNativeControlSupported(ControlType::SpinButtons, ControlPart::Entire))
147 Rectangle aArea = rSpinbuttonValue.maUpperRect.GetUnion(rSpinbuttonValue.maLowerRect);
148 // only paint the standalone spin buttons, all buttons are painted at once
149 bNativeOK = rRenderContext.DrawNativeControl(ControlType::SpinButtons, ControlPart::AllButtons, aArea,
150 ControlState::ENABLED, rSpinbuttonValue, OUString());
152 return bNativeOK;
157 void ImplDrawSpinButton(vcl::RenderContext& rRenderContext, vcl::Window* pWindow,
158 const Rectangle& rUpperRect, const Rectangle& rLowerRect,
159 bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
160 bool bHorz, bool bMirrorHorz)
162 bool bNativeOK = false;
164 if (pWindow)
166 // are we drawing standalone spin buttons or members of a spinfield ?
167 ControlType aControl = ControlType::SpinButtons;
168 switch (pWindow->GetType())
170 case WINDOW_EDIT:
171 case WINDOW_MULTILINEEDIT:
172 case WINDOW_PATTERNFIELD:
173 case WINDOW_METRICFIELD:
174 case WINDOW_CURRENCYFIELD:
175 case WINDOW_DATEFIELD:
176 case WINDOW_TIMEFIELD:
177 case WINDOW_LONGCURRENCYFIELD:
178 case WINDOW_NUMERICFIELD:
179 case WINDOW_SPINFIELD:
180 aControl = ControlType::Spinbox;
181 break;
182 default:
183 aControl = ControlType::SpinButtons;
184 break;
187 SpinbuttonValue aValue;
188 ImplGetSpinbuttonValue(pWindow, rUpperRect, rLowerRect,
189 bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
190 bHorz, aValue);
192 if( aControl == ControlType::Spinbox )
193 bNativeOK = ImplDrawNativeSpinfield(rRenderContext, pWindow, aValue);
194 else if( aControl == ControlType::SpinButtons )
195 bNativeOK = ImplDrawNativeSpinbuttons(rRenderContext, aValue);
198 if (bNativeOK)
199 return;
201 ImplDrawUpDownButtons(rRenderContext,
202 rUpperRect, rLowerRect,
203 bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
204 bHorz, bMirrorHorz);
207 void ImplDrawUpDownButtons(vcl::RenderContext& rRenderContext,
208 const Rectangle& rUpperRect, const Rectangle& rLowerRect,
209 bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
210 bool bHorz, bool bMirrorHorz)
212 DecorationView aDecoView(&rRenderContext);
214 SymbolType eType1, eType2;
216 if ( bHorz )
218 eType1 = bMirrorHorz ? SymbolType::SPIN_RIGHT : SymbolType::SPIN_LEFT;
219 eType2 = bMirrorHorz ? SymbolType::SPIN_LEFT : SymbolType::SPIN_RIGHT;
221 else
223 eType1 = SymbolType::SPIN_UP;
224 eType2 = SymbolType::SPIN_DOWN;
227 DrawButtonFlags nStyle = DrawButtonFlags::NoLeftLightBorder;
228 // draw upper/left Button
229 if (bUpperIn)
230 nStyle |= DrawButtonFlags::Pressed;
232 Rectangle aUpRect = aDecoView.DrawButton(rUpperRect, nStyle);
234 nStyle = DrawButtonFlags::NoLeftLightBorder;
235 // draw lower/right Button
236 if (bLowerIn)
237 nStyle |= DrawButtonFlags::Pressed;
239 Rectangle aLowRect = aDecoView.DrawButton(rLowerRect, nStyle);
241 // make use of additional default edge
242 aUpRect.Left()--;
243 aUpRect.Top()--;
244 aUpRect.Right()++;
245 aUpRect.Bottom()++;
246 aLowRect.Left()--;
247 aLowRect.Top()--;
248 aLowRect.Right()++;
249 aLowRect.Bottom()++;
251 // draw into the edge, so that something is visible if the rectangle is too small
252 if (aUpRect.GetHeight() < 4)
254 aUpRect.Right()++;
255 aUpRect.Bottom()++;
256 aLowRect.Right()++;
257 aLowRect.Bottom()++;
260 // calculate Symbol size
261 long nTempSize1 = aUpRect.GetWidth();
262 long nTempSize2 = aLowRect.GetWidth();
263 if (std::abs( nTempSize1-nTempSize2 ) == 1)
265 if (nTempSize1 > nTempSize2)
266 aUpRect.Left()++;
267 else
268 aLowRect.Left()++;
270 nTempSize1 = aUpRect.GetHeight();
271 nTempSize2 = aLowRect.GetHeight();
272 if (std::abs(nTempSize1 - nTempSize2) == 1)
274 if (nTempSize1 > nTempSize2)
275 aUpRect.Top()++;
276 else
277 aLowRect.Top()++;
280 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
282 DrawSymbolFlags nSymStyle = DrawSymbolFlags::NONE;
283 if (!bUpperEnabled)
284 nSymStyle |= DrawSymbolFlags::Disable;
285 aDecoView.DrawSymbol(aUpRect, eType1, rStyleSettings.GetButtonTextColor(), nSymStyle);
287 nSymStyle = DrawSymbolFlags::NONE;
288 if (!bLowerEnabled)
289 nSymStyle |= DrawSymbolFlags::Disable;
290 aDecoView.DrawSymbol(aLowRect, eType2, rStyleSettings.GetButtonTextColor(), nSymStyle);
293 void SpinField::ImplInitSpinFieldData()
295 mpEdit.disposeAndClear();
296 mbSpin = false;
297 mbRepeat = false;
298 mbUpperIn = false;
299 mbLowerIn = false;
300 mbInitialUp = false;
301 mbInitialDown = false;
302 mbNoSelect = false;
303 mbInDropDown = false;
306 void SpinField::ImplInit(vcl::Window* pParent, WinBits nWinStyle)
308 Edit::ImplInit( pParent, nWinStyle );
310 if (nWinStyle & (WB_SPIN | WB_DROPDOWN))
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(*this, 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.SetTimeoutHdl(LINK( this, SpinField, ImplTimeout));
333 maRepeatTimer.SetTimeout(GetSettings().GetMouseSettings().GetButtonStartRepeat());
334 if (nWinStyle & WB_REPEAT)
335 mbRepeat = true;
337 SetCompoundControl(true);
341 SpinField::SpinField(vcl::Window* pParent, WinBits nWinStyle) :
342 Edit(WINDOW_SPINFIELD)
344 ImplInitSpinFieldData();
345 ImplInit(pParent, nWinStyle);
348 SpinField::~SpinField()
350 disposeOnce();
353 void SpinField::dispose()
355 mpEdit.disposeAndClear();
357 Edit::dispose();
360 void SpinField::Up()
362 ImplCallEventListenersAndHandler( VCLEVENT_SPINFIELD_UP, [this] () { maUpHdlLink.Call(*this); } );
365 void SpinField::Down()
367 ImplCallEventListenersAndHandler( VCLEVENT_SPINFIELD_DOWN, [this] () { maDownHdlLink.Call(*this); } );
370 void SpinField::First()
372 ImplCallEventListenersAndHandler( VCLEVENT_SPINFIELD_FIRST, [this] () { maFirstHdlLink.Call(*this); } );
375 void SpinField::Last()
377 ImplCallEventListenersAndHandler( VCLEVENT_SPINFIELD_LAST, [this] () { maLastHdlLink.Call(*this); } );
380 void SpinField::MouseButtonDown( const MouseEvent& rMEvt )
382 if (!HasFocus() && (!mpEdit || !mpEdit->HasFocus()))
384 mbNoSelect = true;
385 GrabFocus();
388 if (!IsReadOnly())
390 if (maUpperRect.IsInside(rMEvt.GetPosPixel()))
392 mbUpperIn = true;
393 mbInitialUp = true;
394 Invalidate(maUpperRect);
396 else if (maLowerRect.IsInside(rMEvt.GetPosPixel()))
398 mbLowerIn = true;
399 mbInitialDown = true;
400 Invalidate(maLowerRect);
402 else if (maDropDownRect.IsInside(rMEvt.GetPosPixel()))
404 // put DropDownButton to the right
405 mbInDropDown = ShowDropDown( !mbInDropDown );
406 Invalidate(Rectangle(Point(), GetOutputSizePixel()));
409 if (mbUpperIn || mbLowerIn)
411 Update();
412 CaptureMouse();
413 if (mbRepeat)
414 maRepeatTimer.Start();
415 return;
419 Edit::MouseButtonDown(rMEvt);
422 void SpinField::MouseButtonUp(const MouseEvent& rMEvt)
424 ReleaseMouse();
425 mbInitialUp = mbInitialDown = false;
426 maRepeatTimer.Stop();
427 maRepeatTimer.SetTimeout(GetSettings().GetMouseSettings().GetButtonStartRepeat());
429 if (mbUpperIn)
431 mbUpperIn = false;
432 Invalidate(maUpperRect);
433 Update();
434 Up();
436 else if (mbLowerIn)
438 mbLowerIn = false;
439 Invalidate(maLowerRect);
440 Update();
441 Down();
444 Edit::MouseButtonUp(rMEvt);
447 void SpinField::MouseMove(const MouseEvent& rMEvt)
449 if (rMEvt.IsLeft())
451 if (mbInitialUp)
453 bool bNewUpperIn = maUpperRect.IsInside(rMEvt.GetPosPixel());
454 if (bNewUpperIn != mbUpperIn)
456 if (bNewUpperIn)
458 if (mbRepeat)
459 maRepeatTimer.Start();
461 else
462 maRepeatTimer.Stop();
464 mbUpperIn = bNewUpperIn;
465 Invalidate(maUpperRect);
466 Update();
469 else if (mbInitialDown)
471 bool bNewLowerIn = maLowerRect.IsInside(rMEvt.GetPosPixel());
472 if (bNewLowerIn != mbLowerIn)
474 if (bNewLowerIn)
476 if (mbRepeat)
477 maRepeatTimer.Start();
479 else
480 maRepeatTimer.Stop();
482 mbLowerIn = bNewLowerIn;
483 Invalidate(maLowerRect);
484 Update();
489 Edit::MouseMove(rMEvt);
492 bool SpinField::EventNotify(NotifyEvent& rNEvt)
494 bool bDone = false;
495 if (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT)
497 const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
498 if (!IsReadOnly())
500 sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
501 switch (rKEvt.GetKeyCode().GetCode())
503 case KEY_UP:
505 if (!nMod)
507 Up();
508 bDone = true;
511 break;
512 case KEY_DOWN:
514 if (!nMod)
516 Down();
517 bDone = true;
519 else if ((nMod == KEY_MOD2) && !mbInDropDown && (GetStyle() & WB_DROPDOWN))
521 mbInDropDown = ShowDropDown(true);
522 Invalidate(Rectangle(Point(), GetOutputSizePixel()));
523 bDone = true;
526 break;
527 case KEY_PAGEUP:
529 if (!nMod)
531 Last();
532 bDone = true;
535 break;
536 case KEY_PAGEDOWN:
538 if (!nMod)
540 First();
541 bDone = true;
544 break;
549 if (rNEvt.GetType() == MouseNotifyEvent::COMMAND)
551 if ((rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) && !IsReadOnly())
553 MouseWheelBehaviour nWheelBehavior(GetSettings().GetMouseSettings().GetWheelBehavior());
554 if (nWheelBehavior == MouseWheelBehaviour::ALWAYS
555 || (nWheelBehavior == MouseWheelBehaviour::FocusOnly && HasChildPathFocus()))
557 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
558 if (pData->GetMode() == CommandWheelMode::SCROLL)
560 if (pData->GetDelta() < 0)
561 Down();
562 else
563 Up();
564 bDone = true;
567 else
568 bDone = false; // don't eat this event, let the default handling happen (i.e. scroll the context)
572 return bDone || Edit::EventNotify(rNEvt);
575 void SpinField::FillLayoutData() const
577 if (mbSpin)
579 mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
580 AppendLayoutData(*GetSubEdit());
581 GetSubEdit()->SetLayoutDataParent(this);
583 else
584 Edit::FillLayoutData();
587 void SpinField::Paint(vcl::RenderContext& rRenderContext, const Rectangle& rRect)
589 if (mbSpin)
591 bool bEnable = IsEnabled();
592 ImplDrawSpinButton(rRenderContext, this, maUpperRect, maLowerRect,
593 mbUpperIn, mbLowerIn, bEnable, bEnable);
596 if (GetStyle() & WB_DROPDOWN)
598 DecorationView aView(&rRenderContext);
600 DrawButtonFlags nStyle = DrawButtonFlags::NoLightBorder;
601 if (mbInDropDown)
602 nStyle |= DrawButtonFlags::Pressed;
603 Rectangle aInnerRect = aView.DrawButton(maDropDownRect, nStyle);
605 SymbolType eSymbol = SymbolType::SPIN_DOWN;
606 DrawSymbolFlags nSymbolStyle = IsEnabled() ? DrawSymbolFlags::NONE : DrawSymbolFlags::Disable;
607 aView.DrawSymbol(aInnerRect, eSymbol, rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor(), nSymbolStyle);
610 Edit::Paint(rRenderContext, rRect);
613 void SpinField::ImplCalcButtonAreas(OutputDevice* pDev, const Size& rOutSz, Rectangle& rDDArea,
614 Rectangle& rSpinUpArea, Rectangle& rSpinDownArea)
616 const StyleSettings& rStyleSettings = pDev->GetSettings().GetStyleSettings();
618 Size aSize = rOutSz;
619 Size aDropDownSize;
621 if (GetStyle() & WB_DROPDOWN)
623 long nW = rStyleSettings.GetScrollBarSize();
624 nW = GetDrawPixel( pDev, nW );
625 aDropDownSize = Size( CalcZoom( nW ), aSize.Height() );
626 aSize.Width() -= aDropDownSize.Width();
627 rDDArea = Rectangle( Point( aSize.Width(), 0 ), aDropDownSize );
628 rDDArea.Top()--;
630 else
631 rDDArea.SetEmpty();
633 // calcuate sizes according to the height
634 if (GetStyle() & WB_SPIN)
636 long nBottom1 = aSize.Height()/2;
637 long nBottom2 = aSize.Height()-1;
638 long nTop2 = nBottom1;
639 if ( !(aSize.Height() & 0x01) )
640 nBottom1--;
642 bool bNativeRegionOK = false;
643 Rectangle aContentUp, aContentDown;
645 if ((pDev->GetOutDevType() == OUTDEV_WINDOW) &&
646 // there is just no useful native support for spinfields with dropdown
647 ! (GetStyle() & WB_DROPDOWN) &&
648 IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire))
650 vcl::Window *pWin = static_cast<vcl::Window*>(pDev);
651 vcl::Window *pBorder = pWin->GetWindow( GetWindowType::Border );
653 // get the system's spin button size
654 ImplControlValue aControlValue;
655 Rectangle aBound;
656 Point aPoint;
658 // use the full extent of the control
659 Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
661 bNativeRegionOK =
662 pWin->GetNativeControlRegion(ControlType::Spinbox, ControlPart::ButtonUp,
663 aArea, ControlState::NONE, aControlValue, OUString(), aBound, aContentUp) &&
664 pWin->GetNativeControlRegion(ControlType::Spinbox, ControlPart::ButtonDown,
665 aArea, ControlState::NONE, aControlValue, OUString(), aBound, aContentDown);
667 if (bNativeRegionOK)
669 // convert back from border space to local coordinates
670 aPoint = pBorder->ScreenToOutputPixel( pWin->OutputToScreenPixel( aPoint ) );
671 aContentUp.Move(-aPoint.X(), -aPoint.Y());
672 aContentDown.Move(-aPoint.X(), -aPoint.Y());
676 if (bNativeRegionOK)
678 rSpinUpArea = aContentUp;
679 rSpinDownArea = aContentDown;
681 else
683 aSize.Width() -= CalcZoom( GetDrawPixel( pDev, rStyleSettings.GetSpinSize() ) );
685 rSpinUpArea = Rectangle( aSize.Width(), 0, rOutSz.Width()-aDropDownSize.Width()-1, nBottom1 );
686 rSpinDownArea = Rectangle( rSpinUpArea.Left(), nTop2, rSpinUpArea.Right(), nBottom2 );
689 else
691 rSpinUpArea.SetEmpty();
692 rSpinDownArea.SetEmpty();
696 void SpinField::Resize()
698 if (mbSpin)
700 Control::Resize();
701 Size aSize = GetOutputSizePixel();
702 bool bSubEditPositioned = false;
704 if (GetStyle() & (WB_SPIN | WB_DROPDOWN))
706 ImplCalcButtonAreas( this, aSize, maDropDownRect, maUpperRect, maLowerRect );
708 ImplControlValue aControlValue;
709 Point aPoint;
710 Rectangle aContent, aBound;
712 // use the full extent of the control
713 vcl::Window *pBorder = GetWindow( GetWindowType::Border );
714 Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
716 // adjust position and size of the edit field
717 if (GetNativeControlRegion(ControlType::Spinbox, ControlPart::SubEdit, aArea, ControlState::NONE,
718 aControlValue, OUString(), aBound, aContent) &&
719 // there is just no useful native support for spinfields with dropdown
720 !(GetStyle() & WB_DROPDOWN))
722 // convert back from border space to local coordinates
723 aPoint = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aPoint));
724 aContent.Move(-aPoint.X(), -aPoint.Y());
726 // use the themes drop down size
727 mpEdit->SetPosPixel( aContent.TopLeft() );
728 bSubEditPositioned = true;
729 aSize = aContent.GetSize();
731 else
733 if (maUpperRect.IsEmpty())
735 SAL_WARN_IF( maDropDownRect.IsEmpty(), "vcl", "SpinField::Resize: SPIN && DROPDOWN, but all empty rects?" );
736 aSize.Width() = maDropDownRect.Left();
738 else
739 aSize.Width() = maUpperRect.Left();
743 if (!bSubEditPositioned)
745 // this moves our sub edit if RTL gets switched
746 mpEdit->SetPosPixel(Point());
748 mpEdit->SetSizePixel(aSize);
750 if (GetStyle() & WB_SPIN)
751 Invalidate(Rectangle(maUpperRect.TopLeft(), maLowerRect.BottomRight()));
752 if (GetStyle() & WB_DROPDOWN)
753 Invalidate(maDropDownRect);
757 void SpinField::StateChanged(StateChangedType nType)
759 Edit::StateChanged(nType);
761 if (nType == StateChangedType::Enable)
763 if (mbSpin || (GetStyle() & WB_DROPDOWN))
765 mpEdit->Enable(IsEnabled());
767 if (mbSpin)
769 Invalidate(maLowerRect);
770 Invalidate(maUpperRect);
772 if (GetStyle() & WB_DROPDOWN)
773 Invalidate(maDropDownRect);
776 else if (nType == StateChangedType::Style)
778 if (GetStyle() & WB_REPEAT)
779 mbRepeat = true;
780 else
781 mbRepeat = false;
783 else if (nType == StateChangedType::Zoom)
785 Resize();
786 if (mpEdit)
787 mpEdit->SetZoom(GetZoom());
788 Invalidate();
790 else if (nType == StateChangedType::ControlFont)
792 if (mpEdit)
793 mpEdit->SetControlFont(GetControlFont());
794 Invalidate();
796 else if (nType == StateChangedType::ControlForeground)
798 if (mpEdit)
799 mpEdit->SetControlForeground(GetControlForeground());
800 Invalidate();
802 else if (nType == StateChangedType::ControlBackground)
804 if (mpEdit)
805 mpEdit->SetControlBackground(GetControlBackground());
806 Invalidate();
808 else if( nType == StateChangedType::Mirroring )
810 if (mpEdit)
811 mpEdit->CompatStateChanged(StateChangedType::Mirroring);
812 Resize();
816 void SpinField::DataChanged( const DataChangedEvent& rDCEvt )
818 Edit::DataChanged(rDCEvt);
820 if ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
821 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
823 Resize();
824 Invalidate();
828 Rectangle* SpinField::ImplFindPartRect(const Point& rPt)
830 if (maUpperRect.IsInside(rPt))
831 return &maUpperRect;
832 else if (maLowerRect.IsInside(rPt))
833 return &maLowerRect;
834 else
835 return nullptr;
838 bool SpinField::PreNotify(NotifyEvent& rNEvt)
840 const MouseEvent* pMouseEvt = nullptr;
842 if ((rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != nullptr)
844 if (!pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged())
846 // trigger redraw if mouse over state has changed
847 if( IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) ||
848 IsNativeControlSupported(ControlType::Spinbox, ControlPart::AllButtons) )
850 Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
851 Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
852 if( pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()) )
854 // FIXME: this is currently only on OS X
855 // check for other platforms that need similar handling
856 if (ImplGetSVData()->maNWFData.mbNoFocusRects && IsNativeWidgetEnabled() &&
857 IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
859 ImplInvalidateOutermostBorder(this);
861 else
863 // paint directly
864 vcl::Region aRgn( GetActiveClipRegion() );
865 if (pLastRect)
867 SetClipRegion(vcl::Region(*pLastRect));
868 Invalidate(*pLastRect);
869 SetClipRegion( aRgn );
871 if (pRect)
873 SetClipRegion(vcl::Region(*pRect));
874 Invalidate(*pRect);
875 SetClipRegion( aRgn );
883 return Edit::PreNotify(rNEvt);
886 void SpinField::EndDropDown()
888 mbInDropDown = false;
889 Invalidate(Rectangle(Point(), GetOutputSizePixel()));
892 bool SpinField::ShowDropDown( bool )
894 return false;
897 Size SpinField::CalcMinimumSizeForText(const OUString &rString) const
899 Size aSz = Edit::CalcMinimumSizeForText(rString);
901 if ( GetStyle() & WB_DROPDOWN )
902 aSz.Width() += GetSettings().GetStyleSettings().GetScrollBarSize();
903 if ( GetStyle() & WB_SPIN )
905 ImplControlValue aControlValue;
906 Rectangle aArea( Point(), Size(100, aSz.Height()));
907 Rectangle aEntireBound, aEntireContent, aEditBound, aEditContent;
908 if (
909 GetNativeControlRegion(ControlType::Spinbox, ControlPart::Entire,
910 aArea, ControlState::NONE, aControlValue, OUString(), aEntireBound, aEntireContent) &&
911 GetNativeControlRegion(ControlType::Spinbox, ControlPart::SubEdit,
912 aArea, ControlState::NONE, aControlValue, OUString(), aEditBound, aEditContent)
915 aSz.Width() += (aEntireContent.GetWidth() - aEditContent.GetWidth());
917 else
919 aSz.Width() += maUpperRect.GetWidth();
923 return aSz;
926 Size SpinField::CalcMinimumSize() const
928 return CalcMinimumSizeForText(GetText());
931 Size SpinField::GetOptimalSize() const
933 return CalcMinimumSize();
936 Size SpinField::CalcSize(sal_Int32 nChars) const
938 Size aSz = Edit::CalcSize( nChars );
940 if ( GetStyle() & WB_DROPDOWN )
941 aSz.Width() += GetSettings().GetStyleSettings().GetScrollBarSize();
942 if ( GetStyle() & WB_SPIN )
943 aSz.Width() += GetSettings().GetStyleSettings().GetSpinSize();
945 return aSz;
948 IMPL_LINK( SpinField, ImplTimeout, Timer*, pTimer, void )
950 if ( pTimer->GetTimeout() == GetSettings().GetMouseSettings().GetButtonStartRepeat() )
952 pTimer->SetTimeout( GetSettings().GetMouseSettings().GetButtonRepeat() );
953 pTimer->Start();
955 else
957 if ( mbInitialUp )
958 Up();
959 else
960 Down();
964 void SpinField::Draw(OutputDevice* pDev, const Point& rPos, const Size& rSize, DrawFlags nFlags)
966 Edit::Draw(pDev, rPos, rSize, nFlags);
968 WinBits nFieldStyle = GetStyle();
969 if ( !(nFlags & DrawFlags::NoControls ) && ( nFieldStyle & (WB_SPIN|WB_DROPDOWN) ) )
971 Point aPos = pDev->LogicToPixel( rPos );
972 Size aSize = pDev->LogicToPixel( rSize );
973 OutDevType eOutDevType = pDev->GetOutDevType();
974 AllSettings aOldSettings = pDev->GetSettings();
976 pDev->Push();
977 pDev->SetMapMode();
979 if (eOutDevType == OUTDEV_PRINTER)
981 StyleSettings aStyleSettings = aOldSettings.GetStyleSettings();
982 aStyleSettings.SetFaceColor(COL_LIGHTGRAY);
983 aStyleSettings.SetButtonTextColor(COL_BLACK);
984 AllSettings aSettings(aOldSettings);
985 aSettings.SetStyleSettings(aStyleSettings);
986 pDev->SetSettings(aSettings);
989 Rectangle aDD, aUp, aDown;
990 ImplCalcButtonAreas(pDev, aSize, aDD, aUp, aDown);
991 aDD.Move(aPos.X(), aPos.Y());
992 aUp.Move(aPos.X(), aPos.Y());
993 aUp.Top()++;
994 aDown.Move(aPos.X(), aPos.Y());
996 Color aButtonTextColor;
997 if ((nFlags & DrawFlags::Mono) || (eOutDevType == OUTDEV_PRINTER))
998 aButtonTextColor = Color( COL_BLACK );
999 else
1000 aButtonTextColor = GetSettings().GetStyleSettings().GetButtonTextColor();
1002 if (GetStyle() & WB_DROPDOWN)
1004 DecorationView aView( pDev );
1005 DrawButtonFlags nStyle = DrawButtonFlags::NoLightBorder;
1006 Rectangle aInnerRect = aView.DrawButton( aDD, nStyle );
1007 SymbolType eSymbol = SymbolType::SPIN_DOWN;
1008 DrawSymbolFlags nSymbolStyle = (IsEnabled() || (nFlags & DrawFlags::NoDisable)) ? DrawSymbolFlags::NONE : DrawSymbolFlags::Disable;
1009 aView.DrawSymbol(aInnerRect, eSymbol, aButtonTextColor, nSymbolStyle);
1012 if (GetStyle() & WB_SPIN)
1014 ImplDrawSpinButton(*pDev, this, aUp, aDown, false, false);
1017 pDev->Pop();
1018 pDev->SetSettings(aOldSettings);
1022 FactoryFunction SpinField::GetUITestFactory() const
1024 return SpinFieldUIObject::create;
1027 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */