summaryrefslogtreecommitdiffstats
path: root/vcl/source/control/spinfld.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/source/control/spinfld.cxx
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/control/spinfld.cxx')
-rw-r--r--vcl/source/control/spinfld.cxx1034
1 files changed, 1034 insertions, 0 deletions
diff --git a/vcl/source/control/spinfld.cxx b/vcl/source/control/spinfld.cxx
new file mode 100644
index 000000000..6bc1fac29
--- /dev/null
+++ b/vcl/source/control/spinfld.cxx
@@ -0,0 +1,1034 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/toolkit/spinfld.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/uitest/uiobject.hxx>
+#include <sal/log.hxx>
+
+#include <spin.hxx>
+#include <svdata.hxx>
+
+namespace {
+
+void ImplGetSpinbuttonValue(vcl::Window* pWin,
+ const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
+ bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
+ bool bHorz, SpinbuttonValue& rValue )
+{
+ // convert spinbutton data to a SpinbuttonValue structure for native painting
+
+ rValue.maUpperRect = rUpperRect;
+ rValue.maLowerRect = rLowerRect;
+
+ Point aPointerPos = pWin->GetPointerPosPixel();
+
+ ControlState nState = ControlState::ENABLED;
+ if (bUpperIn)
+ nState |= ControlState::PRESSED;
+ if (!pWin->IsEnabled() || !bUpperEnabled)
+ nState &= ~ControlState::ENABLED;
+ if (pWin->HasFocus())
+ nState |= ControlState::FOCUSED;
+ if (pWin->IsMouseOver() && rUpperRect.Contains(aPointerPos))
+ nState |= ControlState::ROLLOVER;
+ rValue.mnUpperState = nState;
+
+ nState = ControlState::ENABLED;
+ if (bLowerIn)
+ nState |= ControlState::PRESSED;
+ if (!pWin->IsEnabled() || !bLowerEnabled)
+ nState &= ~ControlState::ENABLED;
+ if (pWin->HasFocus())
+ nState |= ControlState::FOCUSED;
+ // for overlapping spins: highlight only one
+ if (pWin->IsMouseOver() && rLowerRect.Contains(aPointerPos) && !rUpperRect.Contains(aPointerPos))
+ nState |= ControlState::ROLLOVER;
+ rValue.mnLowerState = nState;
+
+ rValue.mnUpperPart = bHorz ? ControlPart::ButtonLeft : ControlPart::ButtonUp;
+ rValue.mnLowerPart = bHorz ? ControlPart::ButtonRight : ControlPart::ButtonDown;
+}
+
+bool ImplDrawNativeSpinfield(vcl::RenderContext& rRenderContext, vcl::Window const * pWin, const SpinbuttonValue& rSpinbuttonValue)
+{
+ bool bNativeOK = false;
+
+ if (rRenderContext.IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) &&
+ // there is just no useful native support for spinfields with dropdown
+ !(pWin->GetStyle() & WB_DROPDOWN))
+ {
+ if (rRenderContext.IsNativeControlSupported(ControlType::Spinbox, rSpinbuttonValue.mnUpperPart) &&
+ rRenderContext.IsNativeControlSupported(ControlType::Spinbox, rSpinbuttonValue.mnLowerPart))
+ {
+ // only paint the embedded spin buttons, all buttons are painted at once
+ tools::Rectangle aUpperAndLowerButtons( rSpinbuttonValue.maUpperRect.GetUnion( rSpinbuttonValue.maLowerRect ) );
+ bNativeOK = rRenderContext.DrawNativeControl(ControlType::Spinbox, ControlPart::AllButtons, aUpperAndLowerButtons,
+ ControlState::ENABLED, rSpinbuttonValue, OUString());
+ }
+ else
+ {
+ // paint the spinbox as a whole, use borderwindow to have proper clipping
+ vcl::Window* pBorder = pWin->GetWindow(GetWindowType::Border);
+
+ // to not overwrite everything, set the button region as clipregion to the border window
+ tools::Rectangle aClipRect(rSpinbuttonValue.maLowerRect);
+ aClipRect.Union(rSpinbuttonValue.maUpperRect);
+
+ vcl::RenderContext* pContext = &rRenderContext;
+ vcl::Region oldRgn;
+ Point aPt;
+ Size aSize(pBorder->GetOutputSizePixel()); // the size of the border window, i.e., the whole control
+ tools::Rectangle aNatRgn(aPt, aSize);
+
+ if (!pWin->SupportsDoubleBuffering())
+ {
+ // convert from screen space to borderwin space
+ aClipRect.SetPos(pBorder->ScreenToOutputPixel(pWin->OutputToScreenPixel(aClipRect.TopLeft())));
+
+ oldRgn = pBorder->GetOutDev()->GetClipRegion();
+ pBorder->GetOutDev()->SetClipRegion(vcl::Region(aClipRect));
+
+ pContext = pBorder->GetOutDev();
+ }
+
+ tools::Rectangle aBound, aContent;
+ if (!ImplGetSVData()->maNWFData.mbCanDrawWidgetAnySize &&
+ pContext->GetNativeControlRegion(ControlType::Spinbox, ControlPart::Entire,
+ aNatRgn, ControlState::NONE, rSpinbuttonValue,
+ aBound, aContent))
+ {
+ aSize = aContent.GetSize();
+ }
+
+ tools::Rectangle aRgn(aPt, aSize);
+ if (pWin->SupportsDoubleBuffering())
+ {
+ // convert from borderwin space, to the pWin's space
+ aRgn.SetPos(pWin->ScreenToOutputPixel(pBorder->OutputToScreenPixel(aRgn.TopLeft())));
+ }
+
+ bNativeOK = pContext->DrawNativeControl(ControlType::Spinbox, ControlPart::Entire, aRgn,
+ ControlState::ENABLED, rSpinbuttonValue, OUString());
+
+ if (!pWin->SupportsDoubleBuffering())
+ pBorder->GetOutDev()->SetClipRegion(oldRgn);
+ }
+ }
+ return bNativeOK;
+}
+
+bool ImplDrawNativeSpinbuttons(vcl::RenderContext& rRenderContext, const SpinbuttonValue& rSpinbuttonValue)
+{
+ bool bNativeOK = false;
+
+ if (rRenderContext.IsNativeControlSupported(ControlType::SpinButtons, ControlPart::Entire))
+ {
+ tools::Rectangle aArea = rSpinbuttonValue.maUpperRect.GetUnion(rSpinbuttonValue.maLowerRect);
+ // only paint the standalone spin buttons, all buttons are painted at once
+ bNativeOK = rRenderContext.DrawNativeControl(ControlType::SpinButtons, ControlPart::AllButtons, aArea,
+ ControlState::ENABLED, rSpinbuttonValue, OUString());
+ }
+ return bNativeOK;
+}
+
+}
+
+void ImplDrawSpinButton(vcl::RenderContext& rRenderContext, vcl::Window* pWindow,
+ const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
+ bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
+ bool bHorz, bool bMirrorHorz)
+{
+ bool bNativeOK = false;
+
+ if (pWindow)
+ {
+ // are we drawing standalone spin buttons or members of a spinfield ?
+ ControlType aControl = ControlType::SpinButtons;
+ switch (pWindow->GetType())
+ {
+ case WindowType::EDIT:
+ case WindowType::MULTILINEEDIT:
+ case WindowType::PATTERNFIELD:
+ case WindowType::METRICFIELD:
+ case WindowType::CURRENCYFIELD:
+ case WindowType::DATEFIELD:
+ case WindowType::TIMEFIELD:
+ case WindowType::SPINFIELD:
+ case WindowType::FORMATTEDFIELD:
+ aControl = ControlType::Spinbox;
+ break;
+ default:
+ aControl = ControlType::SpinButtons;
+ break;
+ }
+
+ SpinbuttonValue aValue;
+ ImplGetSpinbuttonValue(pWindow, rUpperRect, rLowerRect,
+ bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
+ bHorz, aValue);
+
+ if( aControl == ControlType::Spinbox )
+ bNativeOK = ImplDrawNativeSpinfield(rRenderContext, pWindow, aValue);
+ else if( aControl == ControlType::SpinButtons )
+ bNativeOK = ImplDrawNativeSpinbuttons(rRenderContext, aValue);
+ }
+
+ if (bNativeOK)
+ return;
+
+ ImplDrawUpDownButtons(rRenderContext,
+ rUpperRect, rLowerRect,
+ bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
+ bHorz, bMirrorHorz);
+}
+
+void ImplDrawUpDownButtons(vcl::RenderContext& rRenderContext,
+ const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
+ bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
+ bool bHorz, bool bMirrorHorz)
+{
+ DecorationView aDecoView(&rRenderContext);
+
+ SymbolType eType1, eType2;
+
+ if ( bHorz )
+ {
+ eType1 = bMirrorHorz ? SymbolType::SPIN_RIGHT : SymbolType::SPIN_LEFT;
+ eType2 = bMirrorHorz ? SymbolType::SPIN_LEFT : SymbolType::SPIN_RIGHT;
+ }
+ else
+ {
+ eType1 = SymbolType::SPIN_UP;
+ eType2 = SymbolType::SPIN_DOWN;
+ }
+
+ DrawButtonFlags nStyle = DrawButtonFlags::NoLeftLightBorder;
+ // draw upper/left Button
+ if (bUpperIn)
+ nStyle |= DrawButtonFlags::Pressed;
+
+ tools::Rectangle aUpRect = aDecoView.DrawButton(rUpperRect, nStyle);
+
+ nStyle = DrawButtonFlags::NoLeftLightBorder;
+ // draw lower/right Button
+ if (bLowerIn)
+ nStyle |= DrawButtonFlags::Pressed;
+
+ tools::Rectangle aLowRect = aDecoView.DrawButton(rLowerRect, nStyle);
+
+ // make use of additional default edge
+ aUpRect.AdjustLeft( -1 );
+ aUpRect.AdjustTop( -1 );
+ aUpRect.AdjustRight( 1 );
+ aUpRect.AdjustBottom( 1 );
+ aLowRect.AdjustLeft( -1 );
+ aLowRect.AdjustTop( -1 );
+ aLowRect.AdjustRight( 1 );
+ aLowRect.AdjustBottom( 1 );
+
+ // draw into the edge, so that something is visible if the rectangle is too small
+ if (aUpRect.GetHeight() < 4)
+ {
+ aUpRect.AdjustRight( 1 );
+ aUpRect.AdjustBottom( 1 );
+ aLowRect.AdjustRight( 1 );
+ aLowRect.AdjustBottom( 1 );
+ }
+
+ // calculate Symbol size
+ tools::Long nTempSize1 = aUpRect.GetWidth();
+ tools::Long nTempSize2 = aLowRect.GetWidth();
+ if (std::abs( nTempSize1-nTempSize2 ) == 1)
+ {
+ if (nTempSize1 > nTempSize2)
+ aUpRect.AdjustLeft( 1 );
+ else
+ aLowRect.AdjustLeft( 1 );
+ }
+ nTempSize1 = aUpRect.GetHeight();
+ nTempSize2 = aLowRect.GetHeight();
+ if (std::abs(nTempSize1 - nTempSize2) == 1)
+ {
+ if (nTempSize1 > nTempSize2)
+ aUpRect.AdjustTop( 1 );
+ else
+ aLowRect.AdjustTop( 1 );
+ }
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ DrawSymbolFlags nSymStyle = DrawSymbolFlags::NONE;
+ if (!bUpperEnabled)
+ nSymStyle |= DrawSymbolFlags::Disable;
+ aDecoView.DrawSymbol(aUpRect, eType1, rStyleSettings.GetButtonTextColor(), nSymStyle);
+
+ nSymStyle = DrawSymbolFlags::NONE;
+ if (!bLowerEnabled)
+ nSymStyle |= DrawSymbolFlags::Disable;
+ aDecoView.DrawSymbol(aLowRect, eType2, rStyleSettings.GetButtonTextColor(), nSymStyle);
+}
+
+void SpinField::ImplInitSpinFieldData()
+{
+ mpEdit.disposeAndClear();
+ mbSpin = false;
+ mbRepeat = false;
+ mbUpperIn = false;
+ mbLowerIn = false;
+ mbInitialUp = false;
+ mbInitialDown = false;
+ mbInDropDown = false;
+ mbUpperEnabled = true;
+ mbLowerEnabled = true;
+}
+
+void SpinField::ImplInit(vcl::Window* pParent, WinBits nWinStyle)
+{
+ Edit::ImplInit( pParent, nWinStyle );
+
+ if (!(nWinStyle & (WB_SPIN | WB_DROPDOWN)))
+ return;
+
+ mbSpin = true;
+
+ // Some themes want external spin buttons, therefore the main
+ // spinfield should not overdraw the border between its encapsulated
+ // edit field and the spin buttons
+ if ((nWinStyle & WB_SPIN) && ImplUseNativeBorder(*GetOutDev(), nWinStyle))
+ {
+ SetBackground();
+ mpEdit.set(VclPtr<Edit>::Create(this, WB_NOBORDER));
+ mpEdit->SetBackground();
+ }
+ else
+ mpEdit.set(VclPtr<Edit>::Create(this, WB_NOBORDER));
+
+ mpEdit->EnableRTL(false);
+ mpEdit->SetPosPixel(Point());
+ mpEdit->Show();
+
+ SetSubEdit(mpEdit);
+
+ maRepeatTimer.SetInvokeHandler(LINK( this, SpinField, ImplTimeout));
+ maRepeatTimer.SetTimeout(MouseSettings::GetButtonStartRepeat());
+ if (nWinStyle & WB_REPEAT)
+ mbRepeat = true;
+
+ SetCompoundControl(true);
+}
+
+SpinField::SpinField(vcl::Window* pParent, WinBits nWinStyle, WindowType nType) :
+ Edit(nType), maRepeatTimer("SpinField maRepeatTimer")
+{
+ ImplInitSpinFieldData();
+ ImplInit(pParent, nWinStyle);
+}
+
+SpinField::~SpinField()
+{
+ disposeOnce();
+}
+
+void SpinField::dispose()
+{
+ mpEdit.disposeAndClear();
+
+ Edit::dispose();
+}
+
+void SpinField::Up()
+{
+ ImplCallEventListenersAndHandler( VclEventId::SpinfieldUp, [this] () { maUpHdlLink.Call(*this); } );
+}
+
+void SpinField::Down()
+{
+ ImplCallEventListenersAndHandler( VclEventId::SpinfieldDown, [this] () { maDownHdlLink.Call(*this); } );
+}
+
+void SpinField::First()
+{
+ ImplCallEventListenersAndHandler(VclEventId::SpinfieldFirst, nullptr);
+}
+
+void SpinField::Last()
+{
+ ImplCallEventListenersAndHandler(VclEventId::SpinfieldLast, nullptr);
+}
+
+void SpinField::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (!HasFocus() && (!mpEdit || !mpEdit->HasFocus()))
+ {
+ GrabFocus();
+ }
+
+ if (!IsReadOnly())
+ {
+ if (maUpperRect.Contains(rMEvt.GetPosPixel()))
+ {
+ mbUpperIn = true;
+ mbInitialUp = true;
+ Invalidate(maUpperRect);
+ }
+ else if (maLowerRect.Contains(rMEvt.GetPosPixel()))
+ {
+ mbLowerIn = true;
+ mbInitialDown = true;
+ Invalidate(maLowerRect);
+ }
+ else if (maDropDownRect.Contains(rMEvt.GetPosPixel()))
+ {
+ // put DropDownButton to the right
+ mbInDropDown = ShowDropDown( !mbInDropDown );
+ Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
+ }
+
+ if (mbUpperIn || mbLowerIn)
+ {
+ CaptureMouse();
+ if (mbRepeat)
+ maRepeatTimer.Start();
+ return;
+ }
+ }
+
+ Edit::MouseButtonDown(rMEvt);
+}
+
+void SpinField::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ ReleaseMouse();
+ mbInitialUp = mbInitialDown = false;
+ maRepeatTimer.Stop();
+ maRepeatTimer.SetTimeout(MouseSettings::GetButtonStartRepeat());
+
+ if (mbUpperIn)
+ {
+ mbUpperIn = false;
+ Invalidate(maUpperRect);
+ Up();
+ }
+ else if (mbLowerIn)
+ {
+ mbLowerIn = false;
+ Invalidate(maLowerRect);
+ Down();
+ }
+
+ Edit::MouseButtonUp(rMEvt);
+}
+
+void SpinField::MouseMove(const MouseEvent& rMEvt)
+{
+ if (rMEvt.IsLeft())
+ {
+ if (mbInitialUp)
+ {
+ bool bNewUpperIn = maUpperRect.Contains(rMEvt.GetPosPixel());
+ if (bNewUpperIn != mbUpperIn)
+ {
+ if (bNewUpperIn)
+ {
+ if (mbRepeat)
+ maRepeatTimer.Start();
+ }
+ else
+ maRepeatTimer.Stop();
+
+ mbUpperIn = bNewUpperIn;
+ Invalidate(maUpperRect);
+ }
+ }
+ else if (mbInitialDown)
+ {
+ bool bNewLowerIn = maLowerRect.Contains(rMEvt.GetPosPixel());
+ if (bNewLowerIn != mbLowerIn)
+ {
+ if (bNewLowerIn)
+ {
+ if (mbRepeat)
+ maRepeatTimer.Start();
+ }
+ else
+ maRepeatTimer.Stop();
+
+ mbLowerIn = bNewLowerIn;
+ Invalidate(maLowerRect);
+ }
+ }
+ }
+
+ Edit::MouseMove(rMEvt);
+}
+
+bool SpinField::EventNotify(NotifyEvent& rNEvt)
+{
+ bool bDone = false;
+ if (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT)
+ {
+ const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
+ if (!IsReadOnly())
+ {
+ sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
+ switch (rKEvt.GetKeyCode().GetCode())
+ {
+ case KEY_UP:
+ {
+ if (!nMod)
+ {
+ Up();
+ bDone = true;
+ }
+ }
+ break;
+ case KEY_DOWN:
+ {
+ if (!nMod)
+ {
+ Down();
+ bDone = true;
+ }
+ else if ((nMod == KEY_MOD2) && !mbInDropDown && (GetStyle() & WB_DROPDOWN))
+ {
+ mbInDropDown = ShowDropDown(true);
+ Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
+ bDone = true;
+ }
+ }
+ break;
+ case KEY_PAGEUP:
+ {
+ if (!nMod)
+ {
+ Last();
+ bDone = true;
+ }
+ }
+ break;
+ case KEY_PAGEDOWN:
+ {
+ if (!nMod)
+ {
+ First();
+ bDone = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (rNEvt.GetType() == MouseNotifyEvent::COMMAND)
+ {
+ if ((rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) && !IsReadOnly())
+ {
+ MouseWheelBehaviour nWheelBehavior(GetSettings().GetMouseSettings().GetWheelBehavior());
+ if (nWheelBehavior == MouseWheelBehaviour::ALWAYS
+ || (nWheelBehavior == MouseWheelBehaviour::FocusOnly && HasChildPathFocus()))
+ {
+ const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
+ if (pData->GetMode() == CommandWheelMode::SCROLL)
+ {
+ if (pData->GetDelta() < 0)
+ Down();
+ else
+ Up();
+ bDone = true;
+
+ if (!HasChildPathFocus())
+ GrabFocus();
+ }
+ }
+ else
+ bDone = false; // don't eat this event, let the default handling happen (i.e. scroll the context)
+ }
+ }
+
+ return bDone || Edit::EventNotify(rNEvt);
+}
+
+void SpinField::FillLayoutData() const
+{
+ if (mbSpin)
+ {
+ mxLayoutData.emplace();
+ AppendLayoutData(*GetSubEdit());
+ GetSubEdit()->SetLayoutDataParent(this);
+ }
+ else
+ Edit::FillLayoutData();
+}
+
+void SpinField::SetUpperEnabled(bool bEnabled)
+{
+ if (mbUpperEnabled == bEnabled)
+ return;
+
+ mbUpperEnabled = bEnabled;
+
+ if (mbSpin)
+ Invalidate(maUpperRect);
+}
+
+void SpinField::SetLowerEnabled(bool bEnabled)
+{
+ if (mbLowerEnabled == bEnabled)
+ return;
+
+ mbLowerEnabled = bEnabled;
+
+ if (mbSpin)
+ Invalidate(maLowerRect);
+}
+
+void SpinField::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ if (mbSpin)
+ {
+ bool bEnabled = IsEnabled();
+ bool bUpperEnabled = bEnabled && IsUpperEnabled();
+ bool bLowerEnabled = bEnabled && IsLowerEnabled();
+ ImplDrawSpinButton(rRenderContext, this, maUpperRect, maLowerRect,
+ mbUpperIn && bUpperEnabled, mbLowerIn && bLowerEnabled,
+ bUpperEnabled, bLowerEnabled);
+ }
+
+ if (GetStyle() & WB_DROPDOWN)
+ {
+ DecorationView aView(&rRenderContext);
+
+ DrawButtonFlags nStyle = DrawButtonFlags::NoLightBorder;
+ if (mbInDropDown)
+ nStyle |= DrawButtonFlags::Pressed;
+ tools::Rectangle aInnerRect = aView.DrawButton(maDropDownRect, nStyle);
+
+ DrawSymbolFlags nSymbolStyle = IsEnabled() ? DrawSymbolFlags::NONE : DrawSymbolFlags::Disable;
+ aView.DrawSymbol(aInnerRect, SymbolType::SPIN_DOWN, rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor(), nSymbolStyle);
+ }
+
+ Edit::Paint(rRenderContext, rRect);
+}
+
+void SpinField::ImplCalcButtonAreas(const OutputDevice* pDev, const Size& rOutSz, tools::Rectangle& rDDArea,
+ tools::Rectangle& rSpinUpArea, tools::Rectangle& rSpinDownArea)
+{
+ const StyleSettings& rStyleSettings = pDev->GetSettings().GetStyleSettings();
+
+ Size aSize = rOutSz;
+ Size aDropDownSize;
+
+ if (GetStyle() & WB_DROPDOWN)
+ {
+ tools::Long nW = rStyleSettings.GetScrollBarSize();
+ nW = GetDrawPixel( pDev, nW );
+ aDropDownSize = Size( CalcZoom( nW ), aSize.Height() );
+ aSize.AdjustWidth( -(aDropDownSize.Width()) );
+ rDDArea = tools::Rectangle( Point( aSize.Width(), 0 ), aDropDownSize );
+ rDDArea.AdjustTop( -1 );
+ }
+ else
+ rDDArea.SetEmpty();
+
+ // calculate sizes according to the height
+ if (GetStyle() & WB_SPIN)
+ {
+ tools::Long nBottom1 = aSize.Height()/2;
+ tools::Long nBottom2 = aSize.Height()-1;
+ tools::Long nTop2 = nBottom1;
+ if ( !(aSize.Height() & 0x01) )
+ nBottom1--;
+
+ bool bNativeRegionOK = false;
+ tools::Rectangle aContentUp, aContentDown;
+
+ if ((pDev->GetOutDevType() == OUTDEV_WINDOW) &&
+ // there is just no useful native support for spinfields with dropdown
+ ! (GetStyle() & WB_DROPDOWN) &&
+ IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire))
+ {
+ vcl::Window *pWin = pDev->GetOwnerWindow();
+ vcl::Window *pBorder = pWin->GetWindow( GetWindowType::Border );
+
+ // get the system's spin button size
+ ImplControlValue aControlValue;
+ tools::Rectangle aBound;
+ Point aPoint;
+
+ // use the full extent of the control
+ tools::Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
+
+ bNativeRegionOK =
+ pWin->GetNativeControlRegion(ControlType::Spinbox, ControlPart::ButtonUp,
+ aArea, ControlState::NONE, aControlValue, aBound, aContentUp) &&
+ pWin->GetNativeControlRegion(ControlType::Spinbox, ControlPart::ButtonDown,
+ aArea, ControlState::NONE, aControlValue, aBound, aContentDown);
+
+ if (bNativeRegionOK)
+ {
+ // convert back from border space to local coordinates
+ aPoint = pBorder->ScreenToOutputPixel( pWin->OutputToScreenPixel( aPoint ) );
+ aContentUp.Move(-aPoint.X(), -aPoint.Y());
+ aContentDown.Move(-aPoint.X(), -aPoint.Y());
+ }
+ }
+
+ if (bNativeRegionOK)
+ {
+ rSpinUpArea = aContentUp;
+ rSpinDownArea = aContentDown;
+ }
+ else
+ {
+ aSize.AdjustWidth( -(CalcZoom( GetDrawPixel( pDev, rStyleSettings.GetSpinSize() ) )) );
+
+ rSpinUpArea = tools::Rectangle( aSize.Width(), 0, rOutSz.Width()-aDropDownSize.Width()-1, nBottom1 );
+ rSpinDownArea = tools::Rectangle( rSpinUpArea.Left(), nTop2, rSpinUpArea.Right(), nBottom2 );
+ }
+ }
+ else
+ {
+ rSpinUpArea.SetEmpty();
+ rSpinDownArea.SetEmpty();
+ }
+}
+
+void SpinField::Resize()
+{
+ if (!mbSpin)
+ return;
+
+ Control::Resize();
+ Size aSize = GetOutputSizePixel();
+ bool bSubEditPositioned = false;
+
+ if (GetStyle() & (WB_SPIN | WB_DROPDOWN))
+ {
+ ImplCalcButtonAreas( GetOutDev(), aSize, maDropDownRect, maUpperRect, maLowerRect );
+
+ ImplControlValue aControlValue;
+ Point aPoint;
+ tools::Rectangle aContent, aBound;
+
+ // use the full extent of the control
+ vcl::Window *pBorder = GetWindow( GetWindowType::Border );
+ tools::Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
+
+ // adjust position and size of the edit field
+ if (GetNativeControlRegion(ControlType::Spinbox, ControlPart::SubEdit, aArea, ControlState::NONE,
+ aControlValue, aBound, aContent) &&
+ // there is just no useful native support for spinfields with dropdown
+ !(GetStyle() & WB_DROPDOWN))
+ {
+ // convert back from border space to local coordinates
+ aPoint = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aPoint));
+ aContent.Move(-aPoint.X(), -aPoint.Y());
+
+ // use the themes drop down size
+ mpEdit->SetPosPixel( aContent.TopLeft() );
+ bSubEditPositioned = true;
+ aSize = aContent.GetSize();
+ }
+ else
+ {
+ if (maUpperRect.IsEmpty())
+ {
+ SAL_WARN_IF( maDropDownRect.IsEmpty(), "vcl", "SpinField::Resize: SPIN && DROPDOWN, but all empty rects?" );
+ aSize.setWidth( maDropDownRect.Left() );
+ }
+ else
+ aSize.setWidth( maUpperRect.Left() );
+ }
+ }
+
+ if (!bSubEditPositioned)
+ {
+ // this moves our sub edit if RTL gets switched
+ mpEdit->SetPosPixel(Point());
+ }
+ mpEdit->SetSizePixel(aSize);
+
+ if (GetStyle() & WB_SPIN)
+ Invalidate(tools::Rectangle(maUpperRect.TopLeft(), maLowerRect.BottomRight()));
+ if (GetStyle() & WB_DROPDOWN)
+ Invalidate(maDropDownRect);
+}
+
+void SpinField::StateChanged(StateChangedType nType)
+{
+ Edit::StateChanged(nType);
+
+ if (nType == StateChangedType::Enable)
+ {
+ if (mbSpin || (GetStyle() & WB_DROPDOWN))
+ {
+ mpEdit->Enable(IsEnabled());
+
+ if (mbSpin)
+ {
+ Invalidate(maLowerRect);
+ Invalidate(maUpperRect);
+ }
+ if (GetStyle() & WB_DROPDOWN)
+ Invalidate(maDropDownRect);
+ }
+ }
+ else if (nType == StateChangedType::Style)
+ {
+ if (GetStyle() & WB_REPEAT)
+ mbRepeat = true;
+ else
+ mbRepeat = false;
+ }
+ else if (nType == StateChangedType::Zoom)
+ {
+ Resize();
+ if (mpEdit)
+ mpEdit->SetZoom(GetZoom());
+ Invalidate();
+ }
+ else if (nType == StateChangedType::ControlFont)
+ {
+ if (mpEdit)
+ mpEdit->SetControlFont(GetControlFont());
+ Invalidate();
+ }
+ else if (nType == StateChangedType::ControlForeground)
+ {
+ if (mpEdit)
+ mpEdit->SetControlForeground(GetControlForeground());
+ Invalidate();
+ }
+ else if (nType == StateChangedType::ControlBackground)
+ {
+ if (mpEdit)
+ mpEdit->SetControlBackground(GetControlBackground());
+ Invalidate();
+ }
+ else if( nType == StateChangedType::Mirroring )
+ {
+ if (mpEdit)
+ mpEdit->CompatStateChanged(StateChangedType::Mirroring);
+ Resize();
+ }
+}
+
+void SpinField::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Edit::DataChanged(rDCEvt);
+
+ if ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
+ {
+ Resize();
+ Invalidate();
+ }
+}
+
+tools::Rectangle* SpinField::ImplFindPartRect(const Point& rPt)
+{
+ if (maUpperRect.Contains(rPt))
+ return &maUpperRect;
+ else if (maLowerRect.Contains(rPt))
+ return &maLowerRect;
+ else
+ return nullptr;
+}
+
+bool SpinField::PreNotify(NotifyEvent& rNEvt)
+{
+ if (rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE)
+ {
+ const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
+ if (pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged())
+ {
+ // trigger redraw if mouse over state has changed
+ if( IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) ||
+ IsNativeControlSupported(ControlType::Spinbox, ControlPart::AllButtons) )
+ {
+ tools::Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
+ tools::Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
+ if( pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()) )
+ {
+ // FIXME: this is currently only on macOS
+ // check for other platforms that need similar handling
+ if (ImplGetSVData()->maNWFData.mbNoFocusRects && IsNativeWidgetEnabled() &&
+ IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
+ {
+ ImplInvalidateOutermostBorder(this);
+ }
+ else
+ {
+ // paint directly
+ vcl::Region aRgn( GetOutDev()->GetActiveClipRegion() );
+ if (pLastRect)
+ {
+ GetOutDev()->SetClipRegion(vcl::Region(*pLastRect));
+ Invalidate(*pLastRect);
+ GetOutDev()->SetClipRegion( aRgn );
+ }
+ if (pRect)
+ {
+ GetOutDev()->SetClipRegion(vcl::Region(*pRect));
+ Invalidate(*pRect);
+ GetOutDev()->SetClipRegion( aRgn );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return Edit::PreNotify(rNEvt);
+}
+
+void SpinField::EndDropDown()
+{
+ mbInDropDown = false;
+ Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
+}
+
+bool SpinField::ShowDropDown( bool )
+{
+ return false;
+}
+
+Size SpinField::CalcMinimumSizeForText(const OUString &rString) const
+{
+ Size aSz = Edit::CalcMinimumSizeForText(rString);
+
+ if ( GetStyle() & WB_DROPDOWN )
+ aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
+ if ( GetStyle() & WB_SPIN )
+ {
+ ImplControlValue aControlValue;
+ tools::Rectangle aArea( Point(), Size(100, aSz.Height()));
+ tools::Rectangle aEntireBound, aEntireContent, aEditBound, aEditContent;
+ if (
+ GetNativeControlRegion(ControlType::Spinbox, ControlPart::Entire,
+ aArea, ControlState::NONE, aControlValue, aEntireBound, aEntireContent) &&
+ GetNativeControlRegion(ControlType::Spinbox, ControlPart::SubEdit,
+ aArea, ControlState::NONE, aControlValue, aEditBound, aEditContent)
+ )
+ {
+ aSz.AdjustWidth(aEntireContent.GetWidth() - aEditContent.GetWidth());
+ }
+ else
+ {
+ aSz.AdjustWidth(maUpperRect.GetWidth() );
+ }
+ }
+
+ return aSz;
+}
+
+Size SpinField::CalcMinimumSize() const
+{
+ return CalcMinimumSizeForText(GetText());
+}
+
+Size SpinField::GetOptimalSize() const
+{
+ return CalcMinimumSize();
+}
+
+Size SpinField::CalcSize(sal_Int32 nChars) const
+{
+ Size aSz = Edit::CalcSize( nChars );
+
+ if ( GetStyle() & WB_DROPDOWN )
+ aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
+ if ( GetStyle() & WB_SPIN )
+ aSz.AdjustWidth(GetSettings().GetStyleSettings().GetSpinSize() );
+
+ return aSz;
+}
+
+IMPL_LINK( SpinField, ImplTimeout, Timer*, pTimer, void )
+{
+ if ( pTimer->GetTimeout() == static_cast<sal_uInt64>(MouseSettings::GetButtonStartRepeat()) )
+ {
+ pTimer->SetTimeout( GetSettings().GetMouseSettings().GetButtonRepeat() );
+ pTimer->Start();
+ }
+ else
+ {
+ if ( mbInitialUp )
+ Up();
+ else
+ Down();
+ }
+}
+
+void SpinField::Draw(OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags)
+{
+ Edit::Draw(pDev, rPos, nFlags);
+
+ WinBits nFieldStyle = GetStyle();
+ if ( (nFlags & SystemTextColorFlags::NoControls ) || !( nFieldStyle & (WB_SPIN|WB_DROPDOWN) ) )
+ return;
+
+ Point aPos = pDev->LogicToPixel( rPos );
+ Size aSize = GetSizePixel();
+ AllSettings aOldSettings = pDev->GetSettings();
+
+ pDev->Push();
+ pDev->SetMapMode();
+
+ tools::Rectangle aDD, aUp, aDown;
+ ImplCalcButtonAreas(pDev, aSize, aDD, aUp, aDown);
+ aDD.Move(aPos.X(), aPos.Y());
+ aUp.Move(aPos.X(), aPos.Y());
+ aUp.AdjustTop( 1 );
+ aDown.Move(aPos.X(), aPos.Y());
+
+ Color aButtonTextColor;
+ if (nFlags & SystemTextColorFlags::Mono)
+ aButtonTextColor = COL_BLACK;
+ else
+ aButtonTextColor = GetSettings().GetStyleSettings().GetButtonTextColor();
+
+ if (GetStyle() & WB_DROPDOWN)
+ {
+ DecorationView aView( pDev );
+ tools::Rectangle aInnerRect = aView.DrawButton( aDD, DrawButtonFlags::NoLightBorder );
+ DrawSymbolFlags nSymbolStyle = IsEnabled() ? DrawSymbolFlags::NONE : DrawSymbolFlags::Disable;
+ aView.DrawSymbol(aInnerRect, SymbolType::SPIN_DOWN, aButtonTextColor, nSymbolStyle);
+ }
+
+ if (GetStyle() & WB_SPIN)
+ {
+ ImplDrawSpinButton(*pDev, this, aUp, aDown, false, false);
+ }
+
+ pDev->Pop();
+ pDev->SetSettings(aOldSettings);
+
+}
+
+FactoryFunction SpinField::GetUITestFactory() const
+{
+ return SpinFieldUIObject::create;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */