LibreOffice Module vcl (master)  1
spinbtn.cxx
Go to the documentation of this file.
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 <vcl/event.hxx>
21 #include <vcl/toolkit/spin.hxx>
22 #include <vcl/settings.hxx>
23 #include <vcl/vclevent.hxx>
24 
25 #include <spin.hxx>
26 
27 void SpinButton::ImplInit( vcl::Window* pParent, WinBits nStyle )
28 {
29  mbUpperIn = false;
30  mbLowerIn = false;
31  mbInitialUp = false;
32  mbInitialDown = false;
33 
34  mnMinRange = 0;
35  mnMaxRange = 100;
36  mnValue = 0;
37  mnValueStep = 1;
38 
39  maRepeatTimer.SetTimeout(MouseSettings::GetButtonStartRepeat());
40  maRepeatTimer.SetInvokeHandler(LINK(this, SpinButton, ImplTimeout));
41 
42  mbRepeat = 0 != (nStyle & WB_REPEAT);
43 
44  if (nStyle & WB_HSCROLL)
45  mbHorz = true;
46  else
47  mbHorz = false;
48 
49  Control::ImplInit( pParent, nStyle, nullptr );
50 }
51 
52 SpinButton::SpinButton( vcl::Window* pParent, WinBits nStyle )
54  , mbUpperIsFocused(false)
55 {
56  ImplInit(pParent, nStyle);
57 }
58 
59 IMPL_LINK(SpinButton, ImplTimeout, Timer*, pTimer, void)
60 {
61  if (pTimer->GetTimeout() == MouseSettings::GetButtonStartRepeat())
62  {
63  pTimer->SetTimeout( GetSettings().GetMouseSettings().GetButtonRepeat() );
64  pTimer->Start();
65  }
66  else
67  {
68  if (mbInitialUp)
69  Up();
70  else
71  Down();
72  }
73 }
74 
75 void SpinButton::Up()
76 {
77  if (ImplIsUpperEnabled())
78  {
79  mnValue += mnValueStep;
80  CompatStateChanged(StateChangedType::Data);
81 
82  ImplMoveFocus(true);
83  }
84 
85  ImplCallEventListenersAndHandler(VclEventId::SpinbuttonUp, nullptr );
86 }
87 
88 void SpinButton::Down()
89 {
90  if (ImplIsLowerEnabled())
91  {
92  mnValue -= mnValueStep;
93  CompatStateChanged(StateChangedType::Data);
94 
95  ImplMoveFocus(false);
96  }
97 
98  ImplCallEventListenersAndHandler(VclEventId::SpinbuttonDown, nullptr );
99 }
100 
101 void SpinButton::Resize()
102 {
103  Control::Resize();
104 
105  Size aSize(GetOutputSizePixel());
106  tools::Rectangle aRect(Point(), aSize);
107  if (mbHorz)
108  {
109  maLowerRect = tools::Rectangle(0, 0, aSize.Width() / 2, aSize.Height() - 1);
110  maUpperRect = tools::Rectangle(maLowerRect.TopRight(), aRect.BottomRight());
111  }
112  else
113  {
114  maUpperRect = tools::Rectangle(0, 0, aSize.Width() - 1, aSize.Height() / 2);
115  maLowerRect = tools::Rectangle(maUpperRect.BottomLeft(), aRect.BottomRight());
116  }
117 
118  ImplCalcFocusRect(ImplIsUpperEnabled() || !ImplIsLowerEnabled());
119 
120  Invalidate();
121 }
122 
123 void SpinButton::Draw(OutputDevice* pDev, const Point& rPos, const Size& rSize, DrawFlags nFlags)
124 {
125  Point aPos = pDev->LogicToPixel(rPos);
126  Size aSize = pDev->LogicToPixel(rSize);
127 
128  pDev->Push();
129  pDev->SetMapMode();
130  if ( !(nFlags & DrawFlags::Mono) )
131  {
132  // DecoView uses the FaceColor...
133  AllSettings aSettings = pDev->GetSettings();
134  StyleSettings aStyleSettings = aSettings.GetStyleSettings();
135  if ( IsControlBackground() )
136  aStyleSettings.SetFaceColor( GetControlBackground() );
137  else
138  aStyleSettings.SetFaceColor( GetSettings().GetStyleSettings().GetFaceColor() );
139 
140  aSettings.SetStyleSettings( aStyleSettings );
141  pDev->SetSettings( aSettings );
142  }
143 
144  tools::Rectangle aRect( Point( 0, 0 ), aSize );
145  tools::Rectangle aLowerRect, aUpperRect;
146  if ( mbHorz )
147  {
148  aLowerRect = tools::Rectangle( 0, 0, aSize.Width()/2, aSize.Height()-1 );
149  aUpperRect = tools::Rectangle( aLowerRect.TopRight(), aRect.BottomRight() );
150  }
151  else
152  {
153  aUpperRect = tools::Rectangle( 0, 0, aSize.Width()-1, aSize.Height()/2 );
154  aLowerRect = tools::Rectangle( aUpperRect.BottomLeft(), aRect.BottomRight() );
155  }
156 
157  aUpperRect += aPos;
158  aLowerRect += aPos;
159 
160  ImplDrawSpinButton(*pDev, this, aUpperRect, aLowerRect, false, false,
161  IsEnabled() && ImplIsUpperEnabled(),
162  IsEnabled() && ImplIsLowerEnabled(), mbHorz, true);
163  pDev->Pop();
164 }
165 
166 void SpinButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
167 {
168  HideFocus();
169 
170  bool bEnable = IsEnabled();
171  ImplDrawSpinButton(rRenderContext, this, maUpperRect, maLowerRect, mbUpperIn, mbLowerIn,
172  bEnable && ImplIsUpperEnabled(),
173  bEnable && ImplIsLowerEnabled(), mbHorz, true);
174 
175  if (HasFocus())
176  ShowFocus(maFocusRect);
177 }
178 
179 void SpinButton::MouseButtonDown( const MouseEvent& rMEvt )
180 {
181  if ( maUpperRect.IsInside( rMEvt.GetPosPixel() ) && ( ImplIsUpperEnabled() ) )
182  {
183  mbUpperIn = true;
184  mbInitialUp = true;
185  Invalidate( maUpperRect );
186  }
187  else if ( maLowerRect.IsInside( rMEvt.GetPosPixel() ) && ( ImplIsLowerEnabled() ) )
188  {
189  mbLowerIn = true;
190  mbInitialDown = true;
191  Invalidate( maLowerRect );
192  }
193 
194  if ( mbUpperIn || mbLowerIn )
195  {
196  CaptureMouse();
197  if ( mbRepeat )
198  maRepeatTimer.Start();
199  }
200 }
201 
202 void SpinButton::MouseButtonUp( const MouseEvent& )
203 {
204  ReleaseMouse();
205  if ( mbRepeat )
206  {
207  maRepeatTimer.Stop();
208  maRepeatTimer.SetTimeout(MouseSettings::GetButtonStartRepeat() );
209  }
210 
211  if ( mbUpperIn )
212  {
213  mbUpperIn = false;
214  Invalidate( maUpperRect );
215  Up();
216  }
217  else if ( mbLowerIn )
218  {
219  mbLowerIn = false;
220  Invalidate( maLowerRect );
221  Down();
222  }
223 
224  mbInitialUp = mbInitialDown = false;
225 }
226 
227 void SpinButton::MouseMove( const MouseEvent& rMEvt )
228 {
229  if ( !rMEvt.IsLeft() || (!mbInitialUp && !mbInitialDown) )
230  return;
231 
232  if ( !maUpperRect.IsInside( rMEvt.GetPosPixel() ) &&
233  mbUpperIn && mbInitialUp )
234  {
235  mbUpperIn = false;
236  maRepeatTimer.Stop();
237  Invalidate( maUpperRect );
238  }
239  else if ( !maLowerRect.IsInside( rMEvt.GetPosPixel() ) &&
240  mbLowerIn && mbInitialDown )
241  {
242  mbLowerIn = false;
243  maRepeatTimer.Stop();
244  Invalidate( maLowerRect );
245  }
246  else if ( maUpperRect.IsInside( rMEvt.GetPosPixel() ) &&
247  !mbUpperIn && mbInitialUp )
248  {
249  mbUpperIn = true;
250  if ( mbRepeat )
251  maRepeatTimer.Start();
252  Invalidate( maUpperRect );
253  }
254  else if ( maLowerRect.IsInside( rMEvt.GetPosPixel() ) &&
255  !mbLowerIn && mbInitialDown )
256  {
257  mbLowerIn = true;
258  if ( mbRepeat )
259  maRepeatTimer.Start();
260  Invalidate( maLowerRect );
261  }
262 }
263 
264 void SpinButton::KeyInput( const KeyEvent& rKEvt )
265 {
266  if ( !rKEvt.GetKeyCode().GetModifier() )
267  {
268  switch ( rKEvt.GetKeyCode().GetCode() )
269  {
270  case KEY_LEFT:
271  case KEY_RIGHT:
272  {
273  bool bUp = KEY_RIGHT == rKEvt.GetKeyCode().GetCode();
274  if ( mbHorz && !ImplMoveFocus( bUp ) )
275  bUp ? Up() : Down();
276  }
277  break;
278 
279  case KEY_UP:
280  case KEY_DOWN:
281  {
282  bool bUp = KEY_UP == rKEvt.GetKeyCode().GetCode();
283  if ( !mbHorz && !ImplMoveFocus( KEY_UP == rKEvt.GetKeyCode().GetCode() ) )
284  bUp ? Up() : Down();
285  }
286  break;
287 
288  case KEY_SPACE:
289  mbUpperIsFocused ? Up() : Down();
290  break;
291 
292  default:
293  Control::KeyInput( rKEvt );
294  break;
295  }
296  }
297  else
298  Control::KeyInput( rKEvt );
299 }
300 
301 void SpinButton::StateChanged( StateChangedType nType )
302 {
303  switch ( nType )
304  {
307  Invalidate();
308  break;
309 
311  {
312  bool bNewRepeat = 0 != ( GetStyle() & WB_REPEAT );
313  if ( bNewRepeat != mbRepeat )
314  {
315  if ( maRepeatTimer.IsActive() )
316  {
317  maRepeatTimer.Stop();
318  maRepeatTimer.SetTimeout( MouseSettings::GetButtonStartRepeat() );
319  }
320  mbRepeat = bNewRepeat;
321  }
322 
323  bool bNewHorz = 0 != ( GetStyle() & WB_HSCROLL );
324  if ( bNewHorz != mbHorz )
325  {
326  mbHorz = bNewHorz;
327  Resize();
328  }
329  }
330  break;
331  default:;
332  }
333 
334  Control::StateChanged( nType );
335 }
336 
337 void SpinButton::SetRangeMin( long nNewRange )
338 {
339  SetRange( Range( nNewRange, GetRangeMax() ) );
340 }
341 
342 void SpinButton::SetRangeMax( long nNewRange )
343 {
344  SetRange( Range( GetRangeMin(), nNewRange ) );
345 }
346 
347 void SpinButton::SetRange( const Range& rRange )
348 {
349  // adjust rage
350  Range aRange = rRange;
351  aRange.Justify();
352  long nNewMinRange = aRange.Min();
353  long nNewMaxRange = aRange.Max();
354 
355  // do something only if old and new range differ
356  if ( (mnMinRange != nNewMinRange) ||
357  (mnMaxRange != nNewMaxRange) )
358  {
359  mnMinRange = nNewMinRange;
360  mnMaxRange = nNewMaxRange;
361 
362  // adjust value to new range, if necessary
363  if ( mnValue > mnMaxRange )
364  mnValue = mnMaxRange;
365  if ( mnValue < mnMinRange )
366  mnValue = mnMinRange;
367 
368  CompatStateChanged( StateChangedType::Data );
369  }
370 }
371 
372 void SpinButton::SetValue( long nValue )
373 {
374  // adjust, if necessary
375  if ( nValue > mnMaxRange )
376  nValue = mnMaxRange;
377  if ( nValue < mnMinRange )
378  nValue = mnMinRange;
379 
380  if ( mnValue != nValue )
381  {
382  mnValue = nValue;
383  CompatStateChanged( StateChangedType::Data );
384  }
385 }
386 
387 void SpinButton::GetFocus()
388 {
389  ShowFocus( maFocusRect );
391 }
392 
393 void SpinButton::LoseFocus()
394 {
395  HideFocus();
397 }
398 
399 bool SpinButton::ImplMoveFocus( bool _bUpper )
400 {
401  if ( _bUpper == mbUpperIsFocused )
402  return false;
403 
404  HideFocus();
405  ImplCalcFocusRect( _bUpper );
406  if ( HasFocus() )
407  ShowFocus( maFocusRect );
408  return true;
409 }
410 
411 void SpinButton::ImplCalcFocusRect( bool _bUpper )
412 {
413  maFocusRect = _bUpper ? maUpperRect : maLowerRect;
414  // inflate by some pixels
415  maFocusRect.AdjustLeft(2 );
416  maFocusRect.AdjustTop(2 );
417  maFocusRect.AdjustRight( -2 );
418  maFocusRect.AdjustBottom( -2 );
419  mbUpperIsFocused = _bUpper;
420 }
421 
422 tools::Rectangle* SpinButton::ImplFindPartRect( const Point& rPt )
423 {
424  if( maUpperRect.IsInside( rPt ) )
425  return &maUpperRect;
426  else if( maLowerRect.IsInside( rPt ) )
427  return &maLowerRect;
428  else
429  return nullptr;
430 }
431 
432 bool SpinButton::PreNotify( NotifyEvent& rNEvt )
433 {
434  const MouseEvent* pMouseEvt = nullptr;
435 
436  if ((rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE) && (pMouseEvt = rNEvt.GetMouseEvent()) != nullptr)
437  {
438  if (!pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged())
439  {
440  // trigger redraw if mouse over state has changed
441  if (IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) ||
442  IsNativeControlSupported(ControlType::Spinbox, ControlPart::AllButtons) )
443  {
444  tools::Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
445  tools::Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
446  if (pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()))
447  {
448  vcl::Region aRgn(GetActiveClipRegion());
449  if (pLastRect)
450  {
451  SetClipRegion(vcl::Region(*pLastRect));
452  Invalidate(*pLastRect);
453  SetClipRegion( aRgn );
454  }
455  if (pRect)
456  {
457  SetClipRegion(vcl::Region(*pRect));
458  Invalidate(*pRect);
459  SetClipRegion(aRgn);
460  }
461  }
462  }
463  }
464  }
465 
466  return Control::PreNotify(rNEvt);
467 }
468 
469 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
void SetStyleSettings(const StyleSettings &rSet)
long Height() const
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)
Definition: spinfld.cxx:157
Point BottomLeft() const
long AdjustLeft(long nHorzMoveDelta)
const StyleSettings & GetStyleSettings() const
virtual void StateChanged(StateChangedType nStateChange) override
Definition: ctrl.cxx:260
sal_uInt16 GetCode() const
Definition: keycod.hxx:53
virtual void SetSettings(const AllSettings &rSettings)
Definition: outdev.cxx:213
constexpr sal_uInt16 KEY_SPACE
Definition: keycodes.hxx:123
void SetMapMode()
Definition: map.cxx:655
constexpr sal_uInt16 KEY_UP
Definition: keycodes.hxx:111
StateChangedType
Definition: window.hxx:311
sal_Int64 WinBits
sal_uInt16 GetButtons() const
Definition: event.hxx:144
bool IsEnterWindow() const
Definition: event.hxx:135
WinBits const WB_HSCROLL
sal_uInt16 GetModifier() const
Definition: keycod.hxx:56
constexpr sal_uInt16 KEY_DOWN
Definition: keycodes.hxx:110
void Justify()
virtual bool PreNotify(NotifyEvent &rNEvt)
Definition: event.cxx:51
bool IsLeaveWindow() const
Definition: event.hxx:137
DrawFlags
Definition: window.hxx:354
Some things multiple-inherit from VclAbstractDialog and OutputDevice, so we need to use virtual inher...
Definition: outdev.hxx:304
MouseNotifyEvent GetType() const
Definition: event.hxx:307
bool IsInside(const Point &rPOINT) const
const AllSettings & GetSettings() const
Definition: outdev.hxx:420
virtual void LoseFocus()
Definition: window.cxx:1869
WinBits const WB_REPEAT
constexpr sal_uInt16 KEY_RIGHT
Definition: keycodes.hxx:113
Point LogicToPixel(const Point &rLogicPt) const
Definition: map.cxx:934
virtual void KeyInput(const KeyEvent &rKEvt)
Definition: window.cxx:1819
SAL_DLLPRIVATE void ImplInit(vcl::Window *pParent, WinBits nStyle, SystemParentData *pSystemParentData)
Definition: window.cxx:932
bool IsModifierChanged() const
Definition: event.hxx:141
long Max() const
const vcl::KeyCode & GetKeyCode() const
Definition: event.hxx:54
void SetFaceColor(const Color &rColor)
virtual void Resize() override
Definition: ctrl.cxx:74
WindowType
Definition: ctrl.hxx:33
bool IsSynthetic() const
Definition: event.hxx:139
virtual void GetFocus()
Definition: window.cxx:1855
const MouseEvent * GetMouseEvent() const
Definition: event.hxx:323
bool IsLeft() const
Definition: event.hxx:146
const Point & GetPosPixel() const
Definition: event.hxx:120
IMPL_LINK(SpinButton, ImplTimeout, Timer *, pTimer, void)
Definition: spinbtn.cxx:59
Definition: timer.hxx:26
constexpr sal_uInt16 KEY_LEFT
Definition: keycodes.hxx:112
long Min() const
void Push(PushFlags nFlags=PushFlags::ALL)
Definition: outdevstate.cxx:60
Point TopRight() const
const double mnValue
static sal_uLong GetButtonStartRepeat()