Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/control/FormattedField.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <accessibility/svtaccessiblenumericfield.hxx>
21
22
#include <rtl/math.hxx>
23
#include <svl/numformat.hxx>
24
#include <comphelper/processfactory.hxx>
25
#include <comphelper/string.hxx>
26
#include <tools/debug.hxx>
27
#include <tools/json_writer.hxx>
28
29
#include <vcl/builder.hxx>
30
#include <vcl/event.hxx>
31
#include <vcl/commandevent.hxx>
32
#include <vcl/toolkit/fmtfield.hxx>
33
#include <vcl/uitest/formattedfielduiobject.hxx>
34
#include <vcl/weld/SpinButton.hxx>
35
#include <vcl/weld/TimeFormatter.hxx>
36
#include <vcl/weld/weldutils.hxx>
37
38
#include "FieldFormatter.hxx"
39
40
#include <svl/zformat.hxx>
41
42
#include <limits>
43
44
FormattedField::FormattedField(vcl::Window* pParent, WinBits nStyle)
45
0
    : SpinField(pParent, nStyle, WindowType::FORMATTEDFIELD)
46
0
    , m_pFormatter(nullptr)
47
0
{
48
0
}
Unexecuted instantiation: FormattedField::FormattedField(vcl::Window*, long)
Unexecuted instantiation: FormattedField::FormattedField(vcl::Window*, long)
49
50
void FormattedField::dispose()
51
0
{
52
0
    m_pFormatter = nullptr;
53
0
    m_xOwnFormatter.reset();
54
0
    SpinField::dispose();
55
0
}
56
57
rtl::Reference<comphelper::OAccessible> FormattedField::CreateAccessible()
58
0
{
59
0
    return new SVTXAccessibleNumericField(this);
60
0
}
61
62
void FormattedField::SetText(const OUString& rStr)
63
0
{
64
0
    GetFormatter().SetFieldText(rStr, Selection(0, 0));
65
0
}
66
67
void FormattedField::SetText(const OUString& rStr, const Selection& rNewSelection)
68
0
{
69
0
    GetFormatter().SetFieldText(rStr, rNewSelection);
70
0
    SetSelection(rNewSelection);
71
0
}
72
73
bool FormattedField::set_property(const OUString &rKey, const OUString &rValue)
74
0
{
75
0
    if (rKey == "digits")
76
0
        GetFormatter().SetDecimalDigits(rValue.toInt32());
77
0
    else if (rKey == "wrap")
78
0
        GetFormatter().SetWrapOnLimits(toBool(rValue));
79
0
    else
80
0
        return SpinField::set_property(rKey, rValue);
81
0
    return true;
82
0
}
83
84
void FormattedField::Up()
85
0
{
86
0
    Formatter& rFormatter = GetFormatter();
87
0
    auto nScale = weld::SpinButton::Power10(rFormatter.GetDecimalDigits());
88
89
0
    sal_Int64 nValue = std::round(rFormatter.GetValue() * nScale);
90
0
    sal_Int64 nSpinSize = std::round(rFormatter.GetSpinSize() * nScale);
91
0
    assert(nSpinSize != 0);
92
0
    sal_Int64 nRemainder = rFormatter.GetDisableRemainderFactor() || nSpinSize == 0 ? 0 : nValue % nSpinSize;
93
0
    if (nValue >= 0)
94
0
        nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue + nSpinSize - nRemainder;
95
0
    else
96
0
        nValue = (nRemainder == 0) ? nValue + nSpinSize : nValue - nRemainder;
97
98
    // setValue handles under- and overflows (min/max) automatically
99
0
    rFormatter.SetValue(static_cast<double>(nValue) / nScale);
100
0
    SetModifyFlag();
101
0
    Modify();
102
103
0
    SpinField::Up();
104
0
}
105
106
void FormattedField::Down()
107
0
{
108
0
    Formatter& rFormatter = GetFormatter();
109
0
    auto nScale = weld::SpinButton::Power10(rFormatter.GetDecimalDigits());
110
111
0
    sal_Int64 nValue = std::round(rFormatter.GetValue() * nScale);
112
0
    sal_Int64 nSpinSize = std::round(rFormatter.GetSpinSize() * nScale);
113
0
    assert(nSpinSize != 0);
114
0
    sal_Int64 nRemainder = rFormatter.GetDisableRemainderFactor() || nSpinSize == 0 ? 0 : nValue % nSpinSize;
115
0
    if (nValue >= 0)
116
0
        nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nRemainder;
117
0
    else
118
0
        nValue = (nRemainder == 0) ? nValue - nSpinSize : nValue - nSpinSize - nRemainder;
119
120
    // setValue handles under- and overflows (min/max) automatically
121
0
    rFormatter.SetValue(static_cast<double>(nValue) / nScale);
122
0
    SetModifyFlag();
123
0
    Modify();
124
125
0
    SpinField::Down();
126
0
}
127
128
void FormattedField::First()
129
0
{
130
0
    Formatter& rFormatter = GetFormatter();
131
0
    if (rFormatter.HasMinValue())
132
0
    {
133
0
        rFormatter.SetValue(rFormatter.GetMinValue());
134
0
        SetModifyFlag();
135
0
        Modify();
136
0
    }
137
138
0
    SpinField::First();
139
0
}
140
141
void FormattedField::Last()
142
0
{
143
0
    Formatter& rFormatter = GetFormatter();
144
0
    if (rFormatter.HasMaxValue())
145
0
    {
146
0
        rFormatter.SetValue(rFormatter.GetMaxValue());
147
0
        SetModifyFlag();
148
0
        Modify();
149
0
    }
150
151
0
    SpinField::Last();
152
0
}
153
154
void FormattedField::Modify()
155
0
{
156
0
    GetFormatter().Modify();
157
0
}
158
159
bool FormattedField::PreNotify(NotifyEvent& rNEvt)
160
0
{
161
0
    if (rNEvt.GetType() == NotifyEventType::KEYINPUT)
162
0
        GetFormatter().SetLastSelection(GetSelection());
163
0
    return SpinField::PreNotify(rNEvt);
164
0
}
165
166
bool FormattedField::EventNotify(NotifyEvent& rNEvt)
167
0
{
168
0
    if ((rNEvt.GetType() == NotifyEventType::KEYINPUT) && !IsReadOnly())
169
0
    {
170
0
        const KeyEvent& rKEvt = *rNEvt.GetKeyEvent();
171
0
        sal_uInt16 nMod = rKEvt.GetKeyCode().GetModifier();
172
0
        switch ( rKEvt.GetKeyCode().GetCode() )
173
0
        {
174
0
            case KEY_UP:
175
0
            case KEY_DOWN:
176
0
            case KEY_PAGEUP:
177
0
            case KEY_PAGEDOWN:
178
0
            {
179
0
                Formatter& rFormatter = GetFormatter();
180
0
                if (!nMod && rFormatter.GetOrCreateFormatter().IsTextFormat(rFormatter.GetFormatKey()))
181
0
                {
182
                    // the base class would translate this into calls to Up/Down/First/Last,
183
                    // but we don't want this if we are text-formatted
184
0
                    return true;
185
0
                }
186
0
            }
187
0
        }
188
0
    }
189
190
0
    if ((rNEvt.GetType() == NotifyEventType::COMMAND) && !IsReadOnly())
191
0
    {
192
0
        const CommandEvent* pCommand = rNEvt.GetCommandEvent();
193
0
        if (pCommand->GetCommand() == CommandEventId::Wheel)
194
0
        {
195
0
            const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
196
0
            Formatter& rFormatter = GetFormatter();
197
0
            if ((pData->GetMode() == CommandWheelMode::SCROLL) &&
198
0
                rFormatter.GetOrCreateFormatter().IsTextFormat(rFormatter.GetFormatKey()))
199
0
            {
200
                // same as above : prevent the base class from doing Up/Down-calls
201
                // (normally I should put this test into the Up/Down methods itself, shouldn't I ?)
202
                // FS - 71553 - 19.01.00
203
0
                return true;
204
0
            }
205
0
        }
206
0
    }
207
208
0
    if (rNEvt.GetType() == NotifyEventType::LOSEFOCUS && m_pFormatter)
209
0
        m_pFormatter->EntryLostFocus();
210
211
0
    return SpinField::EventNotify( rNEvt );
212
0
}
213
214
Formatter& FormattedField::GetFormatter()
215
0
{
216
0
    if (!m_pFormatter)
217
0
    {
218
0
        m_xOwnFormatter.reset(new FieldFormatter(*this));
219
0
        m_pFormatter = m_xOwnFormatter.get();
220
0
    }
221
0
    return *m_pFormatter;
222
0
}
223
224
void FormattedField::SetFormatter(Formatter* pFormatter)
225
0
{
226
0
    m_xOwnFormatter.reset();
227
0
    m_pFormatter = pFormatter;
228
0
}
229
230
// currently used by online
231
void FormattedField::SetValueFromString(const OUString& rStr)
232
0
{
233
0
    sal_Int32 nEnd;
234
0
    rtl_math_ConversionStatus eStatus;
235
0
    Formatter& rFormatter = GetFormatter();
236
0
    double fValue = ::rtl::math::stringToDouble(rStr, '.', rFormatter.GetDecimalDigits(), &eStatus, &nEnd );
237
238
0
    if (eStatus == rtl_math_ConversionStatus_Ok &&
239
0
        nEnd == rStr.getLength())
240
0
    {
241
0
        rFormatter.SetValue(fValue);
242
0
        SetModifyFlag();
243
0
        Modify();
244
245
        // Notify the value has changed
246
0
        SpinField::Up();
247
0
    }
248
0
    else
249
0
    {
250
0
        SAL_WARN("vcl", "fail to convert the value: " << rStr);
251
0
    }
252
0
}
253
254
FactoryFunction FormattedField::GetUITestFactory() const
255
0
{
256
0
    return FormattedFieldUIObject::create;
257
0
}
258
259
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */