Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/control/spinfld.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/commandevent.hxx>
21
#include <vcl/event.hxx>
22
#include <vcl/decoview.hxx>
23
#include <vcl/rendercontext/SystemTextColorFlags.hxx>
24
#include <vcl/toolkit/spinfld.hxx>
25
#include <vcl/salnativewidgets.hxx>
26
#include <vcl/settings.hxx>
27
#include <vcl/uitest/uiobject.hxx>
28
#include <sal/log.hxx>
29
30
#include <spin.hxx>
31
#include <svdata.hxx>
32
33
namespace {
34
35
void lcl_GetSpinbuttonValue(vcl::Window* pWin,
36
                            const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
37
                            bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
38
                            bool bHorz, SpinbuttonValue& rValue )
39
0
{
40
    // convert spinbutton data to a SpinbuttonValue structure for native painting
41
42
0
    rValue.maUpperRect = rUpperRect;
43
0
    rValue.maLowerRect = rLowerRect;
44
45
0
    Point aPointerPos = pWin->GetPointerPosPixel();
46
47
0
    ControlState nState = ControlState::ENABLED;
48
0
    if (bUpperIn)
49
0
        nState |= ControlState::PRESSED;
50
0
    if (!pWin->IsEnabled() || !bUpperEnabled)
51
0
        nState &= ~ControlState::ENABLED;
52
0
    if (pWin->HasFocus())
53
0
        nState |= ControlState::FOCUSED;
54
0
    if (pWin->IsMouseOver() && rUpperRect.Contains(aPointerPos))
55
0
        nState |= ControlState::ROLLOVER;
56
0
    rValue.mnUpperState = nState;
57
58
0
    nState = ControlState::ENABLED;
59
0
    if (bLowerIn)
60
0
        nState |= ControlState::PRESSED;
61
0
    if (!pWin->IsEnabled() || !bLowerEnabled)
62
0
        nState &= ~ControlState::ENABLED;
63
0
    if (pWin->HasFocus())
64
0
        nState |= ControlState::FOCUSED;
65
    // for overlapping spins: highlight only one
66
0
    if (pWin->IsMouseOver() && rLowerRect.Contains(aPointerPos) && !rUpperRect.Contains(aPointerPos))
67
0
        nState |= ControlState::ROLLOVER;
68
0
    rValue.mnLowerState = nState;
69
70
0
    rValue.mnUpperPart = bHorz ? ControlPart::ButtonLeft : ControlPart::ButtonUp;
71
0
    rValue.mnLowerPart = bHorz ? ControlPart::ButtonRight : ControlPart::ButtonDown;
72
0
}
73
74
bool lcl_DrawNativeSpinfield(vcl::RenderContext& rRenderContext, vcl::Window const * pWin, const SpinbuttonValue& rSpinbuttonValue)
75
0
{
76
0
    bool bNativeOK = false;
77
78
0
    if (rRenderContext.IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) &&
79
        // there is just no useful native support for spinfields with dropdown
80
0
        !(pWin->GetStyle() & WB_DROPDOWN))
81
0
    {
82
0
        if (rRenderContext.IsNativeControlSupported(ControlType::Spinbox, rSpinbuttonValue.mnUpperPart) &&
83
0
            rRenderContext.IsNativeControlSupported(ControlType::Spinbox, rSpinbuttonValue.mnLowerPart))
84
0
        {
85
            // only paint the embedded spin buttons, all buttons are painted at once
86
0
            tools::Rectangle aUpperAndLowerButtons( rSpinbuttonValue.maUpperRect.GetUnion( rSpinbuttonValue.maLowerRect ) );
87
0
            bNativeOK = rRenderContext.DrawNativeControl(ControlType::Spinbox, ControlPart::AllButtons, aUpperAndLowerButtons,
88
0
                                                         ControlState::ENABLED, rSpinbuttonValue, OUString());
89
0
        }
90
0
        else
91
0
        {
92
            // paint the spinbox as a whole, use borderwindow to have proper clipping
93
0
            vcl::Window* pBorder = pWin->GetWindow(GetWindowType::Border);
94
95
            // to not overwrite everything, set the button region as clipregion to the border window
96
0
            tools::Rectangle aClipRect(rSpinbuttonValue.maLowerRect);
97
0
            aClipRect.Union(rSpinbuttonValue.maUpperRect);
98
99
0
            vcl::RenderContext* pContext = &rRenderContext;
100
0
            vcl::Region oldRgn;
101
0
            Point aPt;
102
0
            Size aSize(pBorder->GetOutputSizePixel());    // the size of the border window, i.e., the whole control
103
0
            tools::Rectangle aNatRgn(aPt, aSize);
104
105
0
            if (!pWin->SupportsDoubleBuffering())
106
0
            {
107
                // convert from screen space to borderwin space
108
0
                aClipRect.SetPos(pBorder->ScreenToOutputPixel(pWin->OutputToScreenPixel(aClipRect.TopLeft())));
109
110
0
                oldRgn = pBorder->GetOutDev()->GetClipRegion();
111
0
                pBorder->GetOutDev()->SetClipRegion(vcl::Region(aClipRect));
112
113
0
                pContext = pBorder->GetOutDev();
114
0
            }
115
116
0
            tools::Rectangle aBound, aContent;
117
0
            if (!ImplGetSVData()->maNWFData.mbCanDrawWidgetAnySize &&
118
0
                pContext->GetNativeControlRegion(ControlType::Spinbox, ControlPart::Entire,
119
0
                                                aNatRgn, ControlState::NONE, rSpinbuttonValue,
120
0
                                                aBound, aContent))
121
0
            {
122
0
                aSize = aContent.GetSize();
123
0
            }
124
125
0
            tools::Rectangle aRgn(aPt, aSize);
126
0
            if (pWin->SupportsDoubleBuffering())
127
0
            {
128
                // convert from borderwin space, to the pWin's space
129
0
                aRgn.SetPos(pWin->ScreenToOutputPixel(pBorder->OutputToScreenPixel(aRgn.TopLeft())));
130
0
            }
131
132
0
            bNativeOK = pContext->DrawNativeControl(ControlType::Spinbox, ControlPart::Entire, aRgn,
133
0
                                                   ControlState::ENABLED, rSpinbuttonValue, OUString());
134
135
0
            if (!pWin->SupportsDoubleBuffering())
136
0
                pBorder->GetOutDev()->SetClipRegion(oldRgn);
137
0
        }
138
0
    }
139
0
    return bNativeOK;
140
0
}
141
142
bool lcl_DrawNativeSpinbuttons(vcl::RenderContext& rRenderContext, const SpinbuttonValue& rSpinbuttonValue)
143
0
{
144
0
    bool bNativeOK = false;
145
146
0
    if (rRenderContext.IsNativeControlSupported(ControlType::SpinButtons, ControlPart::Entire))
147
0
    {
148
0
        tools::Rectangle aArea = rSpinbuttonValue.maUpperRect.GetUnion(rSpinbuttonValue.maLowerRect);
149
        // only paint the standalone spin buttons, all buttons are painted at once
150
0
        bNativeOK = rRenderContext.DrawNativeControl(ControlType::SpinButtons, ControlPart::AllButtons, aArea,
151
0
                                                     ControlState::ENABLED, rSpinbuttonValue, OUString());
152
0
    }
153
0
    return bNativeOK;
154
0
}
155
156
}
157
158
void ImplDrawSpinButton(vcl::RenderContext& rRenderContext, vcl::Window* pWindow,
159
                        const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
160
                        bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
161
                        bool bHorz, bool bMirrorHorz)
162
0
{
163
0
    bool bNativeOK = false;
164
165
0
    if (pWindow)
166
0
    {
167
        // are we drawing standalone spin buttons or members of a spinfield ?
168
0
        ControlType aControl = ControlType::SpinButtons;
169
0
        switch (pWindow->GetType())
170
0
        {
171
0
            case WindowType::EDIT:
172
0
            case WindowType::MULTILINEEDIT:
173
0
            case WindowType::PATTERNFIELD:
174
0
            case WindowType::METRICFIELD:
175
0
            case WindowType::CURRENCYFIELD:
176
0
            case WindowType::DATEFIELD:
177
0
            case WindowType::TIMEFIELD:
178
0
            case WindowType::SPINFIELD:
179
0
            case WindowType::FORMATTEDFIELD:
180
0
                aControl = ControlType::Spinbox;
181
0
                break;
182
0
            default:
183
0
                aControl = ControlType::SpinButtons;
184
0
                break;
185
0
        }
186
187
0
        SpinbuttonValue aValue;
188
0
        lcl_GetSpinbuttonValue(pWindow, rUpperRect, rLowerRect,
189
0
                               bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
190
0
                               bHorz, aValue);
191
192
0
        if( aControl == ControlType::Spinbox )
193
0
            bNativeOK = lcl_DrawNativeSpinfield(rRenderContext, pWindow, aValue);
194
0
        else if( aControl == ControlType::SpinButtons )
195
0
            bNativeOK = lcl_DrawNativeSpinbuttons(rRenderContext, aValue);
196
0
    }
197
198
0
    if (bNativeOK)
199
0
        return;
200
201
0
    ImplDrawUpDownButtons(rRenderContext,
202
0
                          rUpperRect, rLowerRect,
203
0
                          bUpperIn, bLowerIn, bUpperEnabled, bLowerEnabled,
204
0
                          bHorz, bMirrorHorz);
205
0
}
206
207
void ImplDrawUpDownButtons(vcl::RenderContext& rRenderContext,
208
                           const tools::Rectangle& rUpperRect, const tools::Rectangle& rLowerRect,
209
                           bool bUpperIn, bool bLowerIn, bool bUpperEnabled, bool bLowerEnabled,
210
                           bool bHorz, bool bMirrorHorz)
211
0
{
212
0
    DecorationView aDecoView(&rRenderContext);
213
214
0
    SymbolType eType1, eType2;
215
216
0
    if ( bHorz )
217
0
    {
218
0
        eType1 = bMirrorHorz ? SymbolType::SPIN_RIGHT : SymbolType::SPIN_LEFT;
219
0
        eType2 = bMirrorHorz ? SymbolType::SPIN_LEFT : SymbolType::SPIN_RIGHT;
220
0
    }
221
0
    else
222
0
    {
223
0
        eType1 = SymbolType::SPIN_UP;
224
0
        eType2 = SymbolType::SPIN_DOWN;
225
0
    }
226
227
0
    DrawButtonFlags nStyle = DrawButtonFlags::NoLeftLightBorder;
228
    // draw upper/left Button
229
0
    if (bUpperIn)
230
0
        nStyle |= DrawButtonFlags::Pressed;
231
232
0
    tools::Rectangle aUpRect = aDecoView.DrawButton(rUpperRect, nStyle);
233
234
0
    nStyle = DrawButtonFlags::NoLeftLightBorder;
235
    // draw lower/right Button
236
0
    if (bLowerIn)
237
0
        nStyle |= DrawButtonFlags::Pressed;
238
239
0
    tools::Rectangle aLowRect = aDecoView.DrawButton(rLowerRect, nStyle);
240
241
     // make use of additional default edge
242
0
    aUpRect.AdjustLeft( -1 );
243
0
    aUpRect.AdjustTop( -1 );
244
0
    aUpRect.AdjustRight( 1 );
245
0
    aUpRect.AdjustBottom( 1 );
246
0
    aLowRect.AdjustLeft( -1 );
247
0
    aLowRect.AdjustTop( -1 );
248
0
    aLowRect.AdjustRight( 1 );
249
0
    aLowRect.AdjustBottom( 1 );
250
251
    // draw into the edge, so that something is visible if the rectangle is too small
252
0
    if (aUpRect.GetHeight() < 4)
253
0
    {
254
0
        aUpRect.AdjustRight( 1 );
255
0
        aUpRect.AdjustBottom( 1 );
256
0
        aLowRect.AdjustRight( 1 );
257
0
        aLowRect.AdjustBottom( 1 );
258
0
    }
259
260
    // calculate Symbol size
261
0
    tools::Long nTempSize1 = aUpRect.GetWidth();
262
0
    tools::Long nTempSize2 = aLowRect.GetWidth();
263
0
    if (std::abs( nTempSize1-nTempSize2 ) == 1)
264
0
    {
265
0
        if (nTempSize1 > nTempSize2)
266
0
            aUpRect.AdjustLeft( 1 );
267
0
        else
268
0
            aLowRect.AdjustLeft( 1 );
269
0
    }
270
0
    nTempSize1 = aUpRect.GetHeight();
271
0
    nTempSize2 = aLowRect.GetHeight();
272
0
    if (std::abs(nTempSize1 - nTempSize2) == 1)
273
0
    {
274
0
        if (nTempSize1 > nTempSize2)
275
0
            aUpRect.AdjustTop( 1 );
276
0
        else
277
0
            aLowRect.AdjustTop( 1 );
278
0
    }
279
280
0
    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
281
282
0
    DrawSymbolFlags nSymStyle = DrawSymbolFlags::NONE;
283
0
    if (!bUpperEnabled)
284
0
        nSymStyle |= DrawSymbolFlags::Disable;
285
0
    aDecoView.DrawSymbol(aUpRect, eType1, rStyleSettings.GetButtonTextColor(), nSymStyle);
286
287
0
    nSymStyle = DrawSymbolFlags::NONE;
288
0
    if (!bLowerEnabled)
289
0
        nSymStyle |= DrawSymbolFlags::Disable;
290
0
    aDecoView.DrawSymbol(aLowRect, eType2, rStyleSettings.GetButtonTextColor(), nSymStyle);
291
0
}
292
293
void SpinField::ImplInitSpinFieldData()
294
0
{
295
0
    mpEdit.disposeAndClear();
296
0
    mbSpin          = false;
297
0
    mbRepeat        = false;
298
0
    mbUpperIn       = false;
299
0
    mbLowerIn       = false;
300
0
    mbInitialUp     = false;
301
0
    mbInitialDown   = false;
302
0
    mbInDropDown    = false;
303
0
    mbUpperEnabled  = true;
304
0
    mbLowerEnabled  = true;
305
0
}
306
307
void SpinField::ImplInit(vcl::Window* pParent, WinBits nWinStyle)
308
0
{
309
0
    Edit::ImplInit( pParent, nWinStyle );
310
311
0
    if (!(nWinStyle & (WB_SPIN | WB_DROPDOWN)))
312
0
        return;
313
314
0
    mbSpin = true;
315
316
    // Some themes want external spin buttons, therefore the main
317
    // spinfield should not overdraw the border between its encapsulated
318
    // edit field and the spin buttons
319
0
    if ((nWinStyle & WB_SPIN) && ImplUseNativeBorder(*GetOutDev(), nWinStyle))
320
0
    {
321
0
        SetBackground();
322
0
        mpEdit.reset(VclPtr<Edit>::Create(this, WB_NOBORDER));
323
0
        mpEdit->SetBackground();
324
0
    }
325
0
    else
326
0
        mpEdit.reset(VclPtr<Edit>::Create(this, WB_NOBORDER));
327
328
0
    mpEdit->EnableRTL(false);
329
0
    mpEdit->SetPosPixel(Point());
330
0
    mpEdit->Show();
331
332
0
    SetSubEdit(mpEdit);
333
334
0
    maRepeatTimer.SetInvokeHandler(LINK( this, SpinField, ImplTimeout));
335
0
    maRepeatTimer.SetTimeout(MouseSettings::GetButtonStartRepeat());
336
0
    if (nWinStyle & WB_REPEAT)
337
0
        mbRepeat = true;
338
339
0
    SetCompoundControl(true);
340
0
}
341
342
SpinField::SpinField(vcl::Window* pParent, WinBits nWinStyle, WindowType eType) :
343
0
    Edit(eType), maRepeatTimer("SpinField maRepeatTimer")
344
0
{
345
0
    ImplInitSpinFieldData();
346
0
    ImplInit(pParent, nWinStyle);
347
0
}
Unexecuted instantiation: SpinField::SpinField(vcl::Window*, long, WindowType)
Unexecuted instantiation: SpinField::SpinField(vcl::Window*, long, WindowType)
348
349
SpinField::~SpinField()
350
0
{
351
0
    disposeOnce();
352
0
}
353
354
void SpinField::dispose()
355
0
{
356
0
    mpEdit.disposeAndClear();
357
358
0
    Edit::dispose();
359
0
}
360
361
void SpinField::Up()
362
0
{
363
0
    ImplCallEventListenersAndHandler( VclEventId::SpinfieldUp, [this] () { maUpHdlLink.Call(*this); } );
364
0
}
365
366
void SpinField::Down()
367
0
{
368
0
    ImplCallEventListenersAndHandler( VclEventId::SpinfieldDown, [this] () { maDownHdlLink.Call(*this); } );
369
0
}
370
371
void SpinField::First()
372
0
{
373
0
    ImplCallEventListenersAndHandler(VclEventId::SpinfieldFirst, nullptr);
374
0
}
375
376
void SpinField::Last()
377
0
{
378
0
    ImplCallEventListenersAndHandler(VclEventId::SpinfieldLast, nullptr);
379
0
}
380
381
void SpinField::MouseButtonDown( const MouseEvent& rMEvt )
382
0
{
383
0
    if (!HasFocus() && (!mpEdit || !mpEdit->HasFocus()))
384
0
    {
385
0
        GrabFocus();
386
0
    }
387
388
0
    if (!IsReadOnly())
389
0
    {
390
0
        if (maUpperRect.Contains(rMEvt.GetPosPixel()))
391
0
        {
392
0
            mbUpperIn   = true;
393
0
            mbInitialUp = true;
394
0
            Invalidate(maUpperRect);
395
0
        }
396
0
        else if (maLowerRect.Contains(rMEvt.GetPosPixel()))
397
0
        {
398
0
            mbLowerIn    = true;
399
0
            mbInitialDown = true;
400
0
            Invalidate(maLowerRect);
401
0
        }
402
0
        else if (maDropDownRect.Contains(rMEvt.GetPosPixel()))
403
0
        {
404
            // put DropDownButton to the right
405
0
            mbInDropDown = ShowDropDown( !mbInDropDown );
406
0
            Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
407
0
        }
408
409
0
        if (mbUpperIn || mbLowerIn)
410
0
        {
411
0
            CaptureMouse();
412
0
            if (mbRepeat)
413
0
                maRepeatTimer.Start();
414
0
            return;
415
0
        }
416
0
    }
417
418
0
    Edit::MouseButtonDown(rMEvt);
419
0
}
420
421
void SpinField::MouseButtonUp(const MouseEvent& rMEvt)
422
0
{
423
0
    ReleaseMouse();
424
0
    mbInitialUp = mbInitialDown = false;
425
0
    maRepeatTimer.Stop();
426
0
    maRepeatTimer.SetTimeout(MouseSettings::GetButtonStartRepeat());
427
428
0
    if (mbUpperIn)
429
0
    {
430
0
        mbUpperIn = false;
431
0
        Invalidate(maUpperRect);
432
0
        Up();
433
0
    }
434
0
    else if (mbLowerIn)
435
0
    {
436
0
        mbLowerIn = false;
437
0
        Invalidate(maLowerRect);
438
0
        Down();
439
0
    }
440
441
0
    Edit::MouseButtonUp(rMEvt);
442
0
}
443
444
void SpinField::MouseMove(const MouseEvent& rMEvt)
445
0
{
446
0
    if (rMEvt.IsLeft())
447
0
    {
448
0
        if (mbInitialUp)
449
0
        {
450
0
            bool bNewUpperIn = maUpperRect.Contains(rMEvt.GetPosPixel());
451
0
            if (bNewUpperIn != mbUpperIn)
452
0
            {
453
0
                if (bNewUpperIn)
454
0
                {
455
0
                    if (mbRepeat)
456
0
                        maRepeatTimer.Start();
457
0
                }
458
0
                else
459
0
                    maRepeatTimer.Stop();
460
461
0
                mbUpperIn = bNewUpperIn;
462
0
                Invalidate(maUpperRect);
463
0
            }
464
0
        }
465
0
        else if (mbInitialDown)
466
0
        {
467
0
            bool bNewLowerIn = maLowerRect.Contains(rMEvt.GetPosPixel());
468
0
            if (bNewLowerIn != mbLowerIn)
469
0
            {
470
0
                if (bNewLowerIn)
471
0
                {
472
0
                    if (mbRepeat)
473
0
                        maRepeatTimer.Start();
474
0
                }
475
0
                else
476
0
                    maRepeatTimer.Stop();
477
478
0
                mbLowerIn = bNewLowerIn;
479
0
                Invalidate(maLowerRect);
480
0
            }
481
0
        }
482
0
    }
483
484
0
    Edit::MouseMove(rMEvt);
485
0
}
486
487
bool SpinField::EventNotify(NotifyEvent& rNEvt)
488
0
{
489
0
    bool bDone = false;
490
0
    if (rNEvt.GetType() == NotifyEventType::KEYINPUT)
491
0
    {
492
0
        const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
493
0
        if (!IsReadOnly())
494
0
        {
495
0
            sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
496
0
            switch (rKEvt.GetKeyCode().GetCode())
497
0
            {
498
0
                case KEY_UP:
499
0
                {
500
0
                    if (!nMod)
501
0
                    {
502
0
                        Up();
503
0
                        bDone = true;
504
0
                    }
505
0
                }
506
0
                break;
507
0
                case KEY_DOWN:
508
0
                {
509
0
                    if (!nMod)
510
0
                    {
511
0
                        Down();
512
0
                        bDone = true;
513
0
                    }
514
0
                    else if ((nMod == KEY_MOD2) && !mbInDropDown && (GetStyle() & WB_DROPDOWN))
515
0
                    {
516
0
                        mbInDropDown = ShowDropDown(true);
517
0
                        Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
518
0
                        bDone = true;
519
0
                    }
520
0
                }
521
0
                break;
522
0
                case KEY_PAGEUP:
523
0
                {
524
0
                    if (!nMod)
525
0
                    {
526
0
                        Last();
527
0
                        bDone = true;
528
0
                    }
529
0
                }
530
0
                break;
531
0
                case KEY_PAGEDOWN:
532
0
                {
533
0
                    if (!nMod)
534
0
                    {
535
0
                        First();
536
0
                        bDone = true;
537
0
                    }
538
0
                }
539
0
                break;
540
0
            }
541
0
        }
542
0
    }
543
544
0
    if (rNEvt.GetType() == NotifyEventType::COMMAND)
545
0
    {
546
0
        if ((rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) && !IsReadOnly())
547
0
        {
548
0
            const tools::Rectangle aRect(Point(0, 0), GetSizePixel());
549
0
            const Point& rMousePos = rNEvt.GetCommandEvent()->GetMousePosPixel();
550
0
            const bool bMouseHovered = aRect.Contains(rMousePos);
551
552
0
            MouseWheelBehaviour nWheelBehavior(GetSettings().GetMouseSettings().GetWheelBehavior());
553
0
            if (bMouseHovered
554
0
                && (nWheelBehavior == MouseWheelBehaviour::ALWAYS
555
0
                    || (nWheelBehavior == MouseWheelBehaviour::FocusOnly && HasChildPathFocus())))
556
0
            {
557
0
                const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
558
0
                if (pData->GetMode() == CommandWheelMode::SCROLL)
559
0
                {
560
0
                    if (pData->GetDelta() < 0)
561
0
                        Down();
562
0
                    else
563
0
                        Up();
564
0
                    bDone = true;
565
566
0
                    if (!HasChildPathFocus())
567
0
                        GrabFocus();
568
0
                }
569
0
            }
570
0
            else
571
0
                bDone = false;  // don't eat this event, let the default handling happen (i.e. scroll the context)
572
0
        }
573
0
    }
574
575
0
    return bDone || Edit::EventNotify(rNEvt);
576
0
}
577
578
void SpinField::FillLayoutData() const
579
0
{
580
0
    if (mbSpin)
581
0
    {
582
0
        mxLayoutData.emplace();
583
0
        AppendLayoutData(*GetSubEdit());
584
0
        GetSubEdit()->SetLayoutDataParent(this);
585
0
    }
586
0
    else
587
0
        Edit::FillLayoutData();
588
0
}
589
590
void SpinField::SetUpperEnabled(bool bEnabled)
591
0
{
592
0
    if (mbUpperEnabled == bEnabled)
593
0
        return;
594
595
0
    mbUpperEnabled = bEnabled;
596
597
0
    if (mbSpin)
598
0
        Invalidate(maUpperRect);
599
0
}
600
601
void SpinField::SetLowerEnabled(bool bEnabled)
602
0
{
603
0
    if (mbLowerEnabled == bEnabled)
604
0
        return;
605
606
0
    mbLowerEnabled = bEnabled;
607
608
0
    if (mbSpin)
609
0
        Invalidate(maLowerRect);
610
0
}
611
612
void SpinField::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
613
0
{
614
0
    if (mbSpin)
615
0
    {
616
0
        bool bEnabled = IsEnabled();
617
0
        bool bUpperEnabled = bEnabled && IsUpperEnabled();
618
0
        bool bLowerEnabled = bEnabled && IsLowerEnabled();
619
0
        ImplDrawSpinButton(rRenderContext, this, maUpperRect, maLowerRect,
620
0
                           mbUpperIn && bUpperEnabled, mbLowerIn && bLowerEnabled,
621
0
                           bUpperEnabled, bLowerEnabled);
622
0
    }
623
624
0
    if (GetStyle() & WB_DROPDOWN)
625
0
    {
626
0
        DecorationView aView(&rRenderContext);
627
628
0
        DrawButtonFlags nStyle = DrawButtonFlags::NoLightBorder;
629
0
        if (mbInDropDown)
630
0
            nStyle |= DrawButtonFlags::Pressed;
631
0
        tools::Rectangle aInnerRect = aView.DrawButton(maDropDownRect, nStyle);
632
633
0
        DrawSymbolFlags nSymbolStyle = IsEnabled() ? DrawSymbolFlags::NONE : DrawSymbolFlags::Disable;
634
0
        aView.DrawSymbol(aInnerRect, SymbolType::SPIN_DOWN, rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor(), nSymbolStyle);
635
0
    }
636
637
0
    Edit::Paint(rRenderContext, rRect);
638
0
}
639
640
void SpinField::ImplCalcButtonAreas(const OutputDevice* pDev, const Size& rOutSz, tools::Rectangle& rDDArea,
641
                                    tools::Rectangle& rSpinUpArea, tools::Rectangle& rSpinDownArea)
642
0
{
643
0
    const StyleSettings& rStyleSettings = pDev->GetSettings().GetStyleSettings();
644
645
0
    Size aSize = rOutSz;
646
0
    Size aDropDownSize;
647
648
0
    if (GetStyle() & WB_DROPDOWN)
649
0
    {
650
0
        tools::Long nW = rStyleSettings.GetScrollBarSize();
651
0
        nW = GetDrawPixel( pDev, nW );
652
0
        aDropDownSize = Size( CalcZoom( nW ), aSize.Height() );
653
0
        aSize.AdjustWidth( -(aDropDownSize.Width()) );
654
0
        rDDArea = tools::Rectangle( Point( aSize.Width(), 0 ), aDropDownSize );
655
0
        rDDArea.AdjustTop( -1 );
656
0
    }
657
0
    else
658
0
        rDDArea.SetEmpty();
659
660
    // calculate sizes according to the height
661
0
    if (GetStyle() & WB_SPIN)
662
0
    {
663
0
        tools::Long nBottom1 = aSize.Height()/2;
664
0
        tools::Long nBottom2 = aSize.Height()-1;
665
0
        tools::Long nTop2 = nBottom1;
666
0
        if ( !(aSize.Height() & 0x01) )
667
0
            nBottom1--;
668
669
0
        bool bNativeRegionOK = false;
670
0
        tools::Rectangle aContentUp, aContentDown;
671
672
0
        if ((pDev->GetOutDevType() == OUTDEV_WINDOW) &&
673
            // there is just no useful native support for spinfields with dropdown
674
0
            ! (GetStyle() & WB_DROPDOWN) &&
675
0
            IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire))
676
0
        {
677
0
            vcl::Window *pWin = pDev->GetOwnerWindow();
678
0
            vcl::Window *pBorder = pWin->GetWindow( GetWindowType::Border );
679
680
            // get the system's spin button size
681
0
            ImplControlValue aControlValue;
682
0
            tools::Rectangle aBound;
683
0
            Point aPoint;
684
685
            // use the full extent of the control
686
0
            tools::Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
687
688
0
            bNativeRegionOK =
689
0
                pWin->GetNativeControlRegion(ControlType::Spinbox, ControlPart::ButtonUp,
690
0
                    aArea, ControlState::NONE, aControlValue, aBound, aContentUp) &&
691
0
                pWin->GetNativeControlRegion(ControlType::Spinbox, ControlPart::ButtonDown,
692
0
                    aArea, ControlState::NONE, aControlValue, aBound, aContentDown);
693
694
0
            if (bNativeRegionOK)
695
0
            {
696
                // convert back from border space to local coordinates
697
0
                aPoint = pBorder->ScreenToOutputPixel( pWin->OutputToScreenPixel( aPoint ) );
698
0
                aContentUp.Move(-aPoint.X(), -aPoint.Y());
699
0
                aContentDown.Move(-aPoint.X(), -aPoint.Y());
700
0
            }
701
0
        }
702
703
0
        if (bNativeRegionOK)
704
0
        {
705
0
            rSpinUpArea = aContentUp;
706
0
            rSpinDownArea = aContentDown;
707
0
        }
708
0
        else
709
0
        {
710
0
            aSize.AdjustWidth( -(CalcZoom( GetDrawPixel( pDev, rStyleSettings.GetSpinSize() ) )) );
711
712
0
            rSpinUpArea = tools::Rectangle( aSize.Width(), 0, rOutSz.Width()-aDropDownSize.Width()-1, nBottom1 );
713
0
            rSpinDownArea = tools::Rectangle( rSpinUpArea.Left(), nTop2, rSpinUpArea.Right(), nBottom2 );
714
0
        }
715
0
    }
716
0
    else
717
0
    {
718
0
        rSpinUpArea.SetEmpty();
719
0
        rSpinDownArea.SetEmpty();
720
0
    }
721
0
}
722
723
void SpinField::Resize()
724
0
{
725
0
    if (!mbSpin)
726
0
        return;
727
728
0
    Control::Resize();
729
0
    Size aSize = GetOutputSizePixel();
730
0
    bool bSubEditPositioned = false;
731
732
0
    if (GetStyle() & (WB_SPIN | WB_DROPDOWN))
733
0
    {
734
0
        ImplCalcButtonAreas( GetOutDev(), aSize, maDropDownRect, maUpperRect, maLowerRect );
735
736
0
        ImplControlValue aControlValue;
737
0
        Point aPoint;
738
0
        tools::Rectangle aContent, aBound;
739
740
        // use the full extent of the control
741
0
        vcl::Window *pBorder = GetWindow( GetWindowType::Border );
742
0
        tools::Rectangle aArea( aPoint, pBorder->GetOutputSizePixel() );
743
744
        // adjust position and size of the edit field
745
0
        if (GetNativeControlRegion(ControlType::Spinbox, ControlPart::SubEdit, aArea, ControlState::NONE,
746
0
                                   aControlValue, aBound, aContent) &&
747
            // there is just no useful native support for spinfields with dropdown
748
0
            !(GetStyle() & WB_DROPDOWN))
749
0
        {
750
            // convert back from border space to local coordinates
751
0
            aPoint = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aPoint));
752
0
            aContent.Move(-aPoint.X(), -aPoint.Y());
753
754
            // use the themes drop down size
755
0
            mpEdit->SetPosPixel( aContent.TopLeft() );
756
0
            bSubEditPositioned = true;
757
0
            aSize = aContent.GetSize();
758
0
        }
759
0
        else
760
0
        {
761
0
            if (maUpperRect.IsEmpty())
762
0
            {
763
0
                SAL_WARN_IF( maDropDownRect.IsEmpty(), "vcl", "SpinField::Resize: SPIN && DROPDOWN, but all empty rects?" );
764
0
                aSize.setWidth( maDropDownRect.Left() );
765
0
            }
766
0
            else
767
0
                aSize.setWidth( maUpperRect.Left() );
768
0
        }
769
0
    }
770
771
0
    if (!bSubEditPositioned)
772
0
    {
773
        // this moves our sub edit if RTL gets switched
774
0
        mpEdit->SetPosPixel(Point());
775
0
    }
776
0
    mpEdit->SetSizePixel(aSize);
777
778
0
    if (GetStyle() & WB_SPIN)
779
0
        Invalidate(tools::Rectangle(maUpperRect.TopLeft(), maLowerRect.BottomRight()));
780
0
    if (GetStyle() & WB_DROPDOWN)
781
0
        Invalidate(maDropDownRect);
782
0
}
783
784
void SpinField::StateChanged(StateChangedType nType)
785
0
{
786
0
    Edit::StateChanged(nType);
787
788
0
    if (nType == StateChangedType::Enable)
789
0
    {
790
0
        if (mbSpin || (GetStyle() & WB_DROPDOWN))
791
0
        {
792
0
            mpEdit->Enable(IsEnabled());
793
794
0
            if (mbSpin)
795
0
            {
796
0
                Invalidate(maLowerRect);
797
0
                Invalidate(maUpperRect);
798
0
            }
799
0
            if (GetStyle() & WB_DROPDOWN)
800
0
                Invalidate(maDropDownRect);
801
0
        }
802
0
    }
803
0
    else if (nType == StateChangedType::Style)
804
0
    {
805
0
        if (GetStyle() & WB_REPEAT)
806
0
            mbRepeat = true;
807
0
        else
808
0
            mbRepeat = false;
809
0
    }
810
0
    else if (nType == StateChangedType::Zoom)
811
0
    {
812
0
        Resize();
813
0
        if (mpEdit)
814
0
            mpEdit->SetZoom(GetZoom());
815
0
        Invalidate();
816
0
    }
817
0
    else if (nType == StateChangedType::ControlFont)
818
0
    {
819
0
        if (mpEdit)
820
0
            mpEdit->SetControlFont(GetControlFont());
821
0
        Invalidate();
822
0
    }
823
0
    else if (nType == StateChangedType::ControlForeground)
824
0
    {
825
0
        if (mpEdit)
826
0
            mpEdit->SetControlForeground(GetControlForeground());
827
0
        Invalidate();
828
0
    }
829
0
    else if (nType == StateChangedType::ControlBackground)
830
0
    {
831
0
        if (mpEdit)
832
0
            mpEdit->SetControlBackground(GetControlBackground());
833
0
        Invalidate();
834
0
    }
835
0
    else if( nType == StateChangedType::Mirroring )
836
0
    {
837
0
        if (mpEdit)
838
0
            mpEdit->CompatStateChanged(StateChangedType::Mirroring);
839
0
        Resize();
840
0
    }
841
0
}
842
843
void SpinField::DataChanged( const DataChangedEvent& rDCEvt )
844
0
{
845
0
    Edit::DataChanged(rDCEvt);
846
847
0
    if ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
848
0
        (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
849
0
    {
850
0
        Resize();
851
0
        Invalidate();
852
0
    }
853
0
}
854
855
tools::Rectangle* SpinField::ImplFindPartRect(const Point& rPt)
856
0
{
857
0
    if (maUpperRect.Contains(rPt))
858
0
        return &maUpperRect;
859
0
    else if (maLowerRect.Contains(rPt))
860
0
        return &maLowerRect;
861
0
    else
862
0
        return nullptr;
863
0
}
864
865
bool SpinField::PreNotify(NotifyEvent& rNEvt)
866
0
{
867
0
    if (rNEvt.GetType() == NotifyEventType::MOUSEMOVE)
868
0
    {
869
0
        const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
870
0
        if (pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged())
871
0
        {
872
            // trigger redraw if mouse over state has changed
873
0
            if( IsNativeControlSupported(ControlType::Spinbox, ControlPart::Entire) ||
874
0
                IsNativeControlSupported(ControlType::Spinbox, ControlPart::AllButtons) )
875
0
            {
876
0
                tools::Rectangle* pRect = ImplFindPartRect( GetPointerPosPixel() );
877
0
                tools::Rectangle* pLastRect = ImplFindPartRect( GetLastPointerPosPixel() );
878
0
                if( pRect != pLastRect || (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow()) )
879
0
                {
880
0
                    if (!IsNativeWidgetEnabled() ||
881
0
                        !IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
882
0
                    {
883
                        // paint directly
884
0
                        vcl::Region aRgn( GetOutDev()->GetActiveClipRegion() );
885
0
                        if (pLastRect)
886
0
                        {
887
0
                            GetOutDev()->SetClipRegion(vcl::Region(*pLastRect));
888
0
                            Invalidate(*pLastRect);
889
0
                            GetOutDev()->SetClipRegion( aRgn );
890
0
                        }
891
0
                        if (pRect)
892
0
                        {
893
0
                            GetOutDev()->SetClipRegion(vcl::Region(*pRect));
894
0
                            Invalidate(*pRect);
895
0
                            GetOutDev()->SetClipRegion( aRgn );
896
0
                        }
897
0
                    }
898
0
                }
899
0
            }
900
0
        }
901
0
    }
902
903
0
    return Edit::PreNotify(rNEvt);
904
0
}
905
906
void SpinField::EndDropDown()
907
0
{
908
0
    mbInDropDown = false;
909
0
    Invalidate(tools::Rectangle(Point(), GetOutputSizePixel()));
910
0
}
911
912
bool SpinField::ShowDropDown( bool )
913
0
{
914
0
    return false;
915
0
}
916
917
Size SpinField::CalcMinimumSizeForText(const OUString &rString) const
918
0
{
919
0
    Size aSz = Edit::CalcMinimumSizeForText(rString);
920
921
0
    if ( GetStyle() & WB_DROPDOWN )
922
0
        aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
923
0
    if ( GetStyle() & WB_SPIN )
924
0
    {
925
0
        ImplControlValue aControlValue;
926
0
        tools::Rectangle aArea( Point(), Size(100, aSz.Height()));
927
0
        tools::Rectangle aEntireBound, aEntireContent, aEditBound, aEditContent;
928
0
        if (
929
0
               GetNativeControlRegion(ControlType::Spinbox, ControlPart::Entire,
930
0
                   aArea, ControlState::NONE, aControlValue, aEntireBound, aEntireContent) &&
931
0
               GetNativeControlRegion(ControlType::Spinbox, ControlPart::SubEdit,
932
0
                   aArea, ControlState::NONE, aControlValue, aEditBound, aEditContent)
933
0
           )
934
0
        {
935
0
            aSz.AdjustWidth(aEntireContent.GetWidth() - aEditContent.GetWidth());
936
0
        }
937
0
        else
938
0
        {
939
0
            aSz.AdjustWidth(maUpperRect.GetWidth() );
940
0
        }
941
0
    }
942
943
0
    return aSz;
944
0
}
945
946
Size SpinField::CalcMinimumSize() const
947
0
{
948
0
    return CalcMinimumSizeForText(GetText());
949
0
}
950
951
Size SpinField::GetOptimalSize() const
952
0
{
953
0
    return CalcMinimumSize();
954
0
}
955
956
Size SpinField::CalcSize(sal_Int32 nChars) const
957
0
{
958
0
    Size aSz = Edit::CalcSize( nChars );
959
960
0
    if ( GetStyle() & WB_DROPDOWN )
961
0
        aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
962
0
    if ( GetStyle() & WB_SPIN )
963
0
        aSz.AdjustWidth(GetSettings().GetStyleSettings().GetSpinSize() );
964
965
0
    return aSz;
966
0
}
967
968
IMPL_LINK( SpinField, ImplTimeout, Timer*, pTimer, void )
969
0
{
970
0
    if ( pTimer->GetTimeout() == static_cast<sal_uInt64>(MouseSettings::GetButtonStartRepeat()) )
971
0
    {
972
0
        pTimer->SetTimeout( GetSettings().GetMouseSettings().GetButtonRepeat() );
973
0
        pTimer->Start();
974
0
    }
975
0
    else
976
0
    {
977
0
        if ( mbInitialUp )
978
0
            Up();
979
0
        else
980
0
            Down();
981
0
    }
982
0
}
983
984
void SpinField::Draw(OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags)
985
0
{
986
0
    Edit::Draw(pDev, rPos, nFlags);
987
988
0
    WinBits nFieldStyle = GetStyle();
989
0
    if ( (nFlags & SystemTextColorFlags::NoControls ) || !( nFieldStyle & (WB_SPIN|WB_DROPDOWN) ) )
990
0
        return;
991
992
0
    Point aPos = pDev->LogicToPixel( rPos );
993
0
    Size aSize = GetSizePixel();
994
0
    AllSettings aOldSettings = pDev->GetSettings();
995
996
0
    pDev->Push();
997
0
    pDev->SetMapMode();
998
999
0
    tools::Rectangle aDD, aUp, aDown;
1000
0
    ImplCalcButtonAreas(pDev, aSize, aDD, aUp, aDown);
1001
0
    aDD.Move(aPos.X(), aPos.Y());
1002
0
    aUp.Move(aPos.X(), aPos.Y());
1003
0
    aUp.AdjustTop( 1 );
1004
0
    aDown.Move(aPos.X(), aPos.Y());
1005
1006
0
    Color aButtonTextColor;
1007
0
    if (nFlags & SystemTextColorFlags::Mono)
1008
0
        aButtonTextColor = COL_BLACK;
1009
0
    else
1010
0
        aButtonTextColor = GetSettings().GetStyleSettings().GetButtonTextColor();
1011
1012
0
    if (GetStyle() & WB_DROPDOWN)
1013
0
    {
1014
0
        DecorationView aView( pDev );
1015
0
        tools::Rectangle aInnerRect = aView.DrawButton( aDD, DrawButtonFlags::NoLightBorder );
1016
0
        DrawSymbolFlags nSymbolStyle = IsEnabled() ? DrawSymbolFlags::NONE : DrawSymbolFlags::Disable;
1017
0
        aView.DrawSymbol(aInnerRect, SymbolType::SPIN_DOWN, aButtonTextColor, nSymbolStyle);
1018
0
    }
1019
1020
0
    if (GetStyle() & WB_SPIN)
1021
0
    {
1022
0
        ImplDrawSpinButton(*pDev, this, aUp, aDown, false, false);
1023
0
    }
1024
1025
0
    pDev->Pop();
1026
0
    pDev->SetSettings(aOldSettings);
1027
1028
0
}
1029
1030
FactoryFunction SpinField::GetUITestFactory() const
1031
0
{
1032
0
    return SpinFieldUIObject::create;
1033
0
}
1034
1035
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */