Coverage Report

Created: 2025-12-31 10:39

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