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/fmtfield.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 <rtl/math.hxx>
21
#include <svl/numformat.hxx>
22
#include <comphelper/processfactory.hxx>
23
#include <comphelper/string.hxx>
24
#include <tools/debug.hxx>
25
26
#include <vcl/builder.hxx>
27
#include <vcl/event.hxx>
28
#include <vcl/commandevent.hxx>
29
#include <vcl/toolkit/fmtfield.hxx>
30
#include <vcl/uitest/formattedfielduiobject.hxx>
31
#include <vcl/weld/weldutils.hxx>
32
33
#include "FieldFormatter.hxx"
34
35
#include <svl/zformat.hxx>
36
37
#include <limits>
38
39
// hmm. No support for regular expression. Well, I always (not really :) wanted to write a finite automat
40
// so here comes a finite automat ...
41
42
namespace validation
43
{
44
    static void lcl_insertStopTransition( StateTransitions& _rRow )
45
0
    {
46
0
        _rRow.insert( Transition( '_', END ) );
47
0
    }
48
49
    static void lcl_insertStartExponentTransition( StateTransitions& _rRow )
50
0
    {
51
0
        _rRow.insert( Transition( 'e', EXPONENT_START ) );
52
0
    }
53
54
    static void lcl_insertSignTransitions( StateTransitions& _rRow, const State eNextState )
55
0
    {
56
0
        _rRow.insert( Transition( '-', eNextState ) );
57
0
        _rRow.insert( Transition( '+', eNextState ) );
58
0
    }
59
60
    static void lcl_insertDigitTransitions( StateTransitions& _rRow, const State eNextState )
61
0
    {
62
0
        for ( sal_Unicode aChar = '0'; aChar <= '9'; ++aChar )
63
0
            _rRow.insert( Transition( aChar, eNextState ) );
64
0
    }
65
66
    static void lcl_insertCommonPreCommaTransitions( StateTransitions& _rRow, const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
67
0
    {
68
        // digits are allowed
69
0
        lcl_insertDigitTransitions( _rRow, DIGIT_PRE_COMMA );
70
71
        // the thousand separator is allowed
72
0
        _rRow.insert( Transition( _cThSep, DIGIT_PRE_COMMA ) );
73
74
        // a comma is allowed
75
0
        _rRow.insert( Transition( _cDecSep, DIGIT_POST_COMMA ) );
76
0
    }
77
78
    NumberValidator::NumberValidator( const sal_Unicode _cThSep, const sal_Unicode _cDecSep )
79
0
    {
80
        // build up our transition table
81
82
        // how to proceed from START
83
0
        {
84
0
            StateTransitions& rRow = m_aTransitions[ START ];
85
0
            rRow.insert( Transition( '_', NUM_START ) );
86
                // if we encounter the normalizing character, we want to proceed with the number
87
0
        }
88
89
        // how to proceed from NUM_START
90
0
        {
91
0
            StateTransitions& rRow = m_aTransitions[ NUM_START ];
92
93
            // a sign is allowed
94
0
            lcl_insertSignTransitions( rRow, DIGIT_PRE_COMMA );
95
96
            // common transitions for the two pre-comma states
97
0
            lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
98
99
            // the exponent may start here
100
            // (this would mean string like "_+e10_", but this is a valid fragment, though no valid number)
101
0
            lcl_insertStartExponentTransition( rRow );
102
0
        }
103
104
        // how to proceed from DIGIT_PRE_COMMA
105
0
        {
106
0
            StateTransitions& rRow = m_aTransitions[ DIGIT_PRE_COMMA ];
107
108
            // common transitions for the two pre-comma states
109
0
            lcl_insertCommonPreCommaTransitions( rRow, _cThSep, _cDecSep );
110
111
            // the exponent may start here
112
0
            lcl_insertStartExponentTransition( rRow );
113
114
            // the final transition indicating the end of the string
115
            // (if there is no comma and no post-comma, then the string may end here)
116
0
            lcl_insertStopTransition( rRow );
117
0
        }
118
119
        // how to proceed from DIGIT_POST_COMMA
120
0
        {
121
0
            StateTransitions& rRow = m_aTransitions[ DIGIT_POST_COMMA ];
122
123
            // there might be digits, which would keep the state at DIGIT_POST_COMMA
124
0
            lcl_insertDigitTransitions( rRow, DIGIT_POST_COMMA );
125
126
            // the exponent may start here
127
0
            lcl_insertStartExponentTransition( rRow );
128
129
            // the string may end here
130
0
            lcl_insertStopTransition( rRow );
131
0
        }
132
133
        // how to proceed from EXPONENT_START
134
0
        {
135
0
            StateTransitions& rRow = m_aTransitions[ EXPONENT_START ];
136
137
            // there may be a sign
138
0
            lcl_insertSignTransitions( rRow, EXPONENT_DIGIT );
139
140
            // there may be digits
141
0
            lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
142
143
            // the string may end here
144
0
            lcl_insertStopTransition( rRow );
145
0
        }
146
147
        // how to proceed from EXPONENT_DIGIT
148
0
        {
149
0
            StateTransitions& rRow = m_aTransitions[ EXPONENT_DIGIT ];
150
151
            // there may be digits
152
0
            lcl_insertDigitTransitions( rRow, EXPONENT_DIGIT );
153
154
            // the string may end here
155
0
            lcl_insertStopTransition( rRow );
156
0
        }
157
158
        // how to proceed from END
159
0
        {
160
0
            /*StateTransitions& rRow =*/ m_aTransitions[ EXPONENT_DIGIT ];
161
            // no valid transition to leave this state
162
            // (note that we, for consistency, nevertheless want to have a row in the table)
163
0
        }
164
0
    }
165
166
    bool NumberValidator::implValidateNormalized( const OUString& _rText )
167
0
    {
168
0
        const sal_Unicode* pCheckPos = _rText.getStr();
169
0
        State eCurrentState = START;
170
171
0
        while ( END != eCurrentState )
172
0
        {
173
            // look up the transition row for the current state
174
0
            TransitionTable::const_iterator aRow = m_aTransitions.find( eCurrentState );
175
0
            DBG_ASSERT( m_aTransitions.end() != aRow,
176
0
                "NumberValidator::implValidateNormalized: invalid transition table (row not found)!" );
177
178
0
            if ( m_aTransitions.end() != aRow )
179
0
            {
180
                // look up the current character in this row
181
0
                StateTransitions::const_iterator aTransition = aRow->second.find( *pCheckPos );
182
0
                if ( aRow->second.end() != aTransition )
183
0
                {
184
                    // there is a valid transition for this character
185
0
                    eCurrentState = aTransition->second;
186
0
                    ++pCheckPos;
187
0
                    continue;
188
0
                }
189
0
            }
190
191
            // if we're here, there is no valid transition
192
0
            break;
193
0
        }
194
195
0
        DBG_ASSERT( ( END != eCurrentState ) || ( 0 == *pCheckPos ),
196
0
            "NumberValidator::implValidateNormalized: inconsistency!" );
197
            // if we're at END, then the string should be done, too - the string should be normalized, means ending
198
            // a "_" and not containing any other "_" (except at the start), and "_" is the only possibility
199
            // to reach the END state
200
201
        // the string is valid if and only if we reached the final state
202
0
        return ( END == eCurrentState );
203
0
    }
204
205
    bool NumberValidator::isValidNumericFragment( std::u16string_view _rText )
206
0
    {
207
0
        if ( _rText.empty() )
208
            // empty strings are always allowed
209
0
            return true;
210
211
        // normalize the string
212
0
        OUString sNormalized = OUString::Concat("_") + _rText + "_";
213
214
0
        return implValidateNormalized( sNormalized );
215
0
    }
216
}
217
218
SvNumberFormatter* Formatter::StaticFormatter::s_cFormatter = nullptr;
219
sal_uLong Formatter::StaticFormatter::s_nReferences = 0;
220
221
SvNumberFormatter* Formatter::StaticFormatter::GetFormatter()
222
0
{
223
0
    if (!s_cFormatter)
224
0
    {
225
        // get the Office's locale and translate
226
0
        LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
227
0
        s_cFormatter = new SvNumberFormatter(
228
0
            ::comphelper::getProcessComponentContext(),
229
0
            eSysLanguage);
230
0
    }
231
0
    return s_cFormatter;
232
0
}
233
234
Formatter::StaticFormatter::StaticFormatter()
235
0
{
236
0
    ++s_nReferences;
237
0
}
238
239
Formatter::StaticFormatter::~StaticFormatter()
240
0
{
241
0
    if (--s_nReferences == 0)
242
0
    {
243
0
        delete s_cFormatter;
244
0
        s_cFormatter = nullptr;
245
0
    }
246
0
}
247
248
Formatter::Formatter()
249
0
    :m_aLastSelection(0,0)
250
0
    ,m_dMinValue(0)
251
0
    ,m_dMaxValue(0)
252
0
    ,m_bHasMin(false)
253
0
    ,m_bHasMax(false)
254
0
    ,m_bWrapOnLimits(false)
255
0
    ,m_bStrictFormat(true)
256
0
    ,m_bEnableEmptyField(true)
257
0
    ,m_bAutoColor(false)
258
0
    ,m_bEnableNaN(false)
259
0
    ,m_bDisableRemainderFactor(false)
260
0
    ,m_bDefaultValueSet(false)
261
0
    ,m_ValueState(valueDirty)
262
0
    ,m_dCurrentValue(0)
263
0
    ,m_dDefaultValue(0)
264
0
    ,m_nFormatKey(0)
265
0
    ,m_pFormatter(nullptr)
266
0
    ,m_dSpinSize(1)
267
0
    ,m_dSpinFirst(-1000000)
268
0
    ,m_dSpinLast(1000000)
269
0
    ,m_bTreatAsNumber(true)
270
0
    ,m_pLastOutputColor(nullptr)
271
0
    ,m_bUseInputStringForFormatting(false)
272
0
{
273
0
}
274
275
Formatter::~Formatter()
276
0
{
277
0
}
278
279
void Formatter::SetFieldText(const OUString& rStr, const Selection& rNewSelection)
280
0
{
281
0
    SetEntryText(rStr, rNewSelection);
282
0
    m_ValueState = valueDirty;
283
0
}
284
285
void Formatter::SetTextFormatted(const OUString& rStr)
286
0
{
287
0
    SAL_INFO_IF(GetOrCreateFormatter().IsTextFormat(m_nFormatKey), "svtools",
288
0
        "FormattedField::SetTextFormatted : valid only with text formats !");
289
290
0
    m_sCurrentTextValue = rStr;
291
292
0
    OUString sFormatted;
293
0
    double dNumber = 0.0;
294
    // IsNumberFormat changes the format key parameter
295
0
    sal_uInt32 nTempFormatKey = static_cast< sal_uInt32 >( m_nFormatKey );
296
0
    if( IsUsingInputStringForFormatting() &&
297
0
        GetOrCreateFormatter().IsNumberFormat(m_sCurrentTextValue, nTempFormatKey, dNumber) )
298
0
    {
299
0
        sFormatted = GetOrCreateFormatter().GetInputLineString(dNumber, m_nFormatKey);
300
0
    }
301
0
    else
302
0
    {
303
0
        GetOrCreateFormatter().GetOutputString(m_sCurrentTextValue,
304
0
                                            m_nFormatKey,
305
0
                                            sFormatted,
306
0
                                            &m_pLastOutputColor);
307
0
    }
308
309
    // calculate the new selection
310
0
    Selection aSel(GetEntrySelection());
311
0
    Selection aNewSel(aSel);
312
0
    aNewSel.Normalize();
313
0
    sal_Int32 nNewLen = sFormatted.getLength();
314
0
    sal_Int32 nCurrentLen = GetEntryText().getLength();
315
0
    if ((nNewLen > nCurrentLen) && (aNewSel.Max() == nCurrentLen))
316
0
    {   // the new text is longer and the cursor was behind the last char (of the old text)
317
0
        if (aNewSel.Min() == 0)
318
0
        {   // the whole text was selected -> select the new text on the whole, too
319
0
            aNewSel.Max() = nNewLen;
320
0
            if (!nCurrentLen)
321
0
            {   // there wasn't really a previous selection (as there was no previous text), we're setting a new one -> check the selection options
322
0
                SelectionOptions nSelOptions = GetEntrySelectionOptions();
323
0
                if (nSelOptions & SelectionOptions::ShowFirst)
324
0
                {   // selection should be from right to left -> swap min and max
325
0
                    aNewSel.Min() = aNewSel.Max();
326
0
                    aNewSel.Max() = 0;
327
0
                }
328
0
            }
329
0
        }
330
0
        else if (aNewSel.Max() == aNewSel.Min())
331
0
        {   // there was no selection -> set the cursor behind the new last char
332
0
            aNewSel.Max() = nNewLen;
333
0
            aNewSel.Min() = nNewLen;
334
0
        }
335
0
    }
336
0
    else if (aNewSel.Max() > nNewLen)
337
0
        aNewSel.Max() = nNewLen;
338
0
    else
339
0
        aNewSel = aSel; // don't use the justified version
340
0
    SetEntryText(sFormatted, aNewSel);
341
0
    m_ValueState = valueString;
342
0
}
343
344
OUString const & Formatter::GetTextValue() const
345
0
{
346
0
    if (m_ValueState != valueString )
347
0
    {
348
0
        const_cast<Formatter*>(this)->m_sCurrentTextValue = GetEntryText();
349
0
        const_cast<Formatter*>(this)->m_ValueState = valueString;
350
0
    }
351
0
    return m_sCurrentTextValue;
352
0
}
353
354
void Formatter::EnableNotANumber(bool _bEnable)
355
0
{
356
0
    if ( m_bEnableNaN == _bEnable )
357
0
        return;
358
359
0
    m_bEnableNaN = _bEnable;
360
0
}
361
362
void Formatter::SetAutoColor(bool _bAutomatic)
363
0
{
364
0
    if (_bAutomatic == m_bAutoColor)
365
0
        return;
366
367
0
    m_bAutoColor = _bAutomatic;
368
0
    if (m_bAutoColor)
369
0
    {
370
        // if auto color is switched on, adjust the current text color, too
371
0
        SetEntryTextColor(m_pLastOutputColor);
372
0
    }
373
0
}
374
375
void Formatter::Modify(bool makeValueDirty)
376
0
{
377
0
    if (!IsStrictFormat())
378
0
    {
379
0
        if(makeValueDirty)
380
0
            m_ValueState = valueDirty;
381
0
        FieldModified();
382
0
        return;
383
0
    }
384
385
0
    OUString sCheck = GetEntryText();
386
0
    if (CheckText(sCheck))
387
0
    {
388
0
        m_sLastValidText = sCheck;
389
0
        m_aLastSelection = GetEntrySelection();
390
0
        if(makeValueDirty)
391
0
            m_ValueState = valueDirty;
392
0
    }
393
0
    else
394
0
    {
395
0
        ImplSetTextImpl(m_sLastValidText, &m_aLastSelection);
396
0
    }
397
398
0
    FieldModified();
399
0
}
400
401
void Formatter::ImplSetTextImpl(const OUString& rNew, Selection const * pNewSel)
402
0
{
403
0
    if (m_bAutoColor)
404
0
        SetEntryTextColor(m_pLastOutputColor);
405
406
0
    if (pNewSel)
407
0
        SetEntryText(rNew, *pNewSel);
408
0
    else
409
0
    {
410
0
        Selection aSel(GetEntrySelection());
411
0
        aSel.Normalize();
412
413
0
        sal_Int32 nNewLen = rNew.getLength();
414
0
        sal_Int32 nCurrentLen = GetEntryText().getLength();
415
416
0
        if ((nNewLen > nCurrentLen) && (aSel.Max() == nCurrentLen))
417
0
        {   // new text is longer and the cursor is behind the last char
418
0
            if (aSel.Min() == 0)
419
0
            {
420
0
                if (!nCurrentLen)
421
0
                {   // there wasn't really a previous selection (as there was no previous text)
422
0
                    aSel.Max() = 0;
423
0
                }
424
0
                else
425
0
                {   // the whole text was selected -> select the new text on the whole, too
426
0
                    aSel.Max() = nNewLen;
427
0
                }
428
0
            }
429
0
            else if (aSel.Max() == aSel.Min())
430
0
            {   // there was no selection -> set the cursor behind the new last char
431
0
                aSel.Max() = nNewLen;
432
0
                aSel.Min() = nNewLen;
433
0
            }
434
0
        }
435
0
        else if (aSel.Max() > nNewLen)
436
0
            aSel.Max() = nNewLen;
437
0
        SetEntryText(rNew, aSel);
438
0
    }
439
440
0
    m_ValueState = valueDirty; // not always necessary, but better re-evaluate for safety reasons
441
0
}
442
443
void Formatter::ImplSetFormatKey(sal_uLong nFormatKey)
444
0
{
445
0
    m_nFormatKey = nFormatKey;
446
0
    bool bNeedFormatter = (m_pFormatter == nullptr) && (nFormatKey != 0);
447
0
    if (bNeedFormatter)
448
0
    {
449
0
        GetOrCreateFormatter(); // this creates a standard formatter
450
0
        assert(m_pFormatter);
451
452
        // It might happen that the standard formatter makes no sense here, but it takes a default
453
        // format. Thus, it is possible to set one of the other standard keys (which are spanning
454
        // across multiple formatters).
455
0
        m_nFormatKey = nFormatKey;
456
        // When calling SetFormatKey without a formatter, the key must be one of the standard values
457
        // that is available for all formatters (and, thus, also in this new one).
458
0
        DBG_ASSERT(m_pFormatter->GetEntry(nFormatKey) != nullptr, "FormattedField::ImplSetFormatKey : invalid format key !");
459
0
    }
460
0
}
461
462
void Formatter::SetFormatKey(sal_uLong nFormatKey)
463
0
{
464
0
    bool bNoFormatter = (m_pFormatter == nullptr);
465
0
    ImplSetFormatKey(nFormatKey);
466
0
    FormatChanged((bNoFormatter && (m_pFormatter != nullptr)) ? FORMAT_CHANGE_TYPE::FORMATTER : FORMAT_CHANGE_TYPE::KEYONLY);
467
0
}
468
469
void Formatter::SetFormatter(SvNumberFormatter* pFormatter, bool bResetFormat)
470
0
{
471
472
0
    if (bResetFormat)
473
0
    {
474
0
        m_pFormatter = pFormatter;
475
476
        // calc the default format key from the Office's UI locale
477
0
        if ( m_pFormatter )
478
0
        {
479
            // get the Office's locale and translate
480
0
            LanguageType eSysLanguage = SvtSysLocale().GetLanguageTag().getLanguageType( false);
481
            // get the standard numeric format for this language
482
0
            m_nFormatKey = m_pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, eSysLanguage );
483
0
        }
484
0
        else
485
0
            m_nFormatKey = 0;
486
0
    }
487
0
    else
488
0
    {
489
0
        LanguageType aOldLang;
490
0
        OUString sOldFormat = GetFormat(aOldLang);
491
492
0
        sal_uInt32 nDestKey = pFormatter->TestNewString(sOldFormat);
493
0
        if (nDestKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
494
0
        {
495
            // language of the new formatter
496
0
            const SvNumberformat* pDefaultEntry = pFormatter->GetEntry(0);
497
0
            LanguageType aNewLang = pDefaultEntry ? pDefaultEntry->GetLanguage() : LANGUAGE_DONTKNOW;
498
499
            // convert the old format string into the new language
500
0
            sal_Int32 nCheckPos;
501
0
            SvNumFormatType nType;
502
0
            pFormatter->PutandConvertEntry(sOldFormat, nCheckPos, nType, nDestKey, aOldLang, aNewLang, true);
503
0
            m_nFormatKey = nDestKey;
504
0
        }
505
0
        m_pFormatter = pFormatter;
506
0
    }
507
508
0
    FormatChanged(FORMAT_CHANGE_TYPE::FORMATTER);
509
0
}
510
511
OUString Formatter::GetFormat(LanguageType& eLang) const
512
0
{
513
0
    const SvNumberformat* pFormatEntry = GetOrCreateFormatter().GetEntry(m_nFormatKey);
514
0
    DBG_ASSERT(pFormatEntry != nullptr, "FormattedField::GetFormat: no number format for the given format key.");
515
0
    OUString sFormatString = pFormatEntry ? pFormatEntry->GetFormatstring() : OUString();
516
0
    eLang = pFormatEntry ? pFormatEntry->GetLanguage() : LANGUAGE_DONTKNOW;
517
518
0
    return sFormatString;
519
0
}
520
521
bool Formatter::SetFormat(const OUString& rFormatString, LanguageType eLang)
522
0
{
523
0
    sal_uInt32 nNewKey = GetOrCreateFormatter().TestNewString(rFormatString, eLang);
524
0
    if (nNewKey == NUMBERFORMAT_ENTRY_NOT_FOUND)
525
0
    {
526
0
        sal_Int32 nCheckPos;
527
0
        SvNumFormatType nType;
528
0
        OUString rFormat(rFormatString);
529
0
        if (!GetOrCreateFormatter().PutEntry(rFormat, nCheckPos, nType, nNewKey, eLang))
530
0
            return false;
531
0
        DBG_ASSERT(nNewKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "FormattedField::SetFormatString : PutEntry returned an invalid key !");
532
0
    }
533
534
0
    if (nNewKey != m_nFormatKey)
535
0
        SetFormatKey(nNewKey);
536
0
    return true;
537
0
}
538
539
OUString Formatter::FormatValue(double fValue)
540
0
{
541
0
    if (m_aFormatValueHdl.IsSet())
542
0
    {
543
0
        std::optional<OUString> aText = m_aFormatValueHdl.Call(fValue);
544
0
        if (aText.has_value())
545
0
            return aText.value();
546
0
    }
547
548
0
    OUString sNewText;
549
0
    if (GetOrCreateFormatter().IsTextFormat(m_nFormatKey))
550
0
    {
551
        // first convert the number as string in standard format
552
0
        OUString sTemp;
553
0
        GetOrCreateFormatter().GetOutputString(fValue, 0, sTemp, &m_pLastOutputColor);
554
        // then encode the string in the corresponding text format
555
0
        GetOrCreateFormatter().GetOutputString(sTemp, m_nFormatKey, sNewText, &m_pLastOutputColor);
556
0
    }
557
0
    else
558
0
    {
559
0
        if( IsUsingInputStringForFormatting())
560
0
        {
561
0
            sNewText = GetOrCreateFormatter().GetInputLineString(fValue, m_nFormatKey);
562
0
        }
563
0
        else
564
0
        {
565
0
            GetOrCreateFormatter().GetOutputString(fValue, m_nFormatKey, sNewText, &m_pLastOutputColor);
566
0
        }
567
0
    }
568
569
0
    return sNewText;
570
0
}
571
572
bool Formatter::GetThousandsSep() const
573
0
{
574
0
    DBG_ASSERT(!GetOrCreateFormatter().IsTextFormat(m_nFormatKey),
575
0
        "FormattedField::GetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
576
577
0
    bool bThousand, IsRed;
578
0
    sal_uInt16 nPrecision, nLeadingCnt;
579
0
    GetOrCreateFormatter().GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
580
581
0
    return bThousand;
582
0
}
583
584
void Formatter::SetThousandsSep(bool _bUseSeparator)
585
0
{
586
0
    DBG_ASSERT(!GetOrCreateFormatter().IsTextFormat(m_nFormatKey),
587
0
        "FormattedField::SetThousandsSep : Are you sure what you are doing when setting the precision of a text format?");
588
589
    // get the current settings
590
0
    bool bThousand, IsRed;
591
0
    sal_uInt16 nPrecision, nLeadingCnt;
592
0
    GetOrCreateFormatter().GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
593
0
    if (bThousand == _bUseSeparator)
594
0
        return;
595
596
    // we need the language for the following
597
0
    LanguageType eLang;
598
0
    GetFormat(eLang);
599
600
    // generate a new format ...
601
0
    OUString sFmtDescription = GetOrCreateFormatter().GenerateFormat(m_nFormatKey, eLang, _bUseSeparator, IsRed, nPrecision, nLeadingCnt);
602
    // ... and introduce it to the formatter
603
0
    sal_Int32 nCheckPos = 0;
604
0
    sal_uInt32 nNewKey;
605
0
    SvNumFormatType nType;
606
0
    GetOrCreateFormatter().PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
607
608
    // set the new key
609
0
    ImplSetFormatKey(nNewKey);
610
0
    FormatChanged(FORMAT_CHANGE_TYPE::THOUSANDSSEP);
611
0
}
612
613
sal_uInt16 Formatter::GetDecimalDigits() const
614
0
{
615
0
    DBG_ASSERT(!GetOrCreateFormatter().IsTextFormat(m_nFormatKey),
616
0
        "FormattedField::GetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
617
618
0
    bool bThousand, IsRed;
619
0
    sal_uInt16 nPrecision, nLeadingCnt;
620
0
    GetOrCreateFormatter().GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
621
622
0
    return nPrecision;
623
0
}
624
625
void Formatter::SetDecimalDigits(sal_uInt16 _nPrecision)
626
0
{
627
0
    DBG_ASSERT(!GetOrCreateFormatter().IsTextFormat(m_nFormatKey),
628
0
        "FormattedField::SetDecimalDigits : Are you sure what you are doing when setting the precision of a text format?");
629
630
    // get the current settings
631
0
    bool bThousand, IsRed;
632
0
    sal_uInt16 nPrecision, nLeadingCnt;
633
0
    GetOrCreateFormatter().GetFormatSpecialInfo(m_nFormatKey, bThousand, IsRed, nPrecision, nLeadingCnt);
634
0
    if (nPrecision == _nPrecision)
635
0
        return;
636
637
    // we need the language for the following
638
0
    LanguageType eLang;
639
0
    GetFormat(eLang);
640
641
    // generate a new format ...
642
0
    OUString sFmtDescription = GetOrCreateFormatter().GenerateFormat(m_nFormatKey, eLang, bThousand, IsRed, _nPrecision, nLeadingCnt);
643
    // ... and introduce it to the formatter
644
0
    sal_Int32 nCheckPos = 0;
645
0
    sal_uInt32 nNewKey;
646
0
    SvNumFormatType nType;
647
0
    GetOrCreateFormatter().PutEntry(sFmtDescription, nCheckPos, nType, nNewKey, eLang);
648
649
    // set the new key
650
0
    ImplSetFormatKey(nNewKey);
651
0
    FormatChanged(FORMAT_CHANGE_TYPE::PRECISION);
652
0
}
653
654
void Formatter::FormatChanged(FORMAT_CHANGE_TYPE _nWhat)
655
0
{
656
0
    m_pLastOutputColor = nullptr;
657
658
0
    if ( (_nWhat == FORMAT_CHANGE_TYPE::FORMATTER) && m_pFormatter )
659
0
        m_pFormatter->SetEvalDateFormat( NfEvalDateFormat::FormatThenInternational );
660
661
0
    ReFormat();
662
0
}
663
664
void Formatter::EntryLostFocus()
665
0
{
666
    // special treatment for empty texts
667
0
    if (GetEntryText().isEmpty())
668
0
    {
669
0
        if (!IsEmptyFieldEnabled())
670
0
        {
671
0
            if (TreatingAsNumber())
672
0
            {
673
0
                ImplSetValue(m_dCurrentValue, true);
674
0
                Modify();
675
0
                m_ValueState = valueDouble;
676
0
            }
677
0
            else
678
0
            {
679
0
                OUString sNew = GetTextValue();
680
0
                if (!sNew.isEmpty())
681
0
                    SetTextFormatted(sNew);
682
0
                else
683
0
                    SetTextFormatted(m_sDefaultText);
684
0
                m_ValueState = valueString;
685
0
            }
686
0
        }
687
0
    }
688
0
    else
689
0
    {
690
0
        Commit();
691
0
    }
692
0
}
693
694
void Formatter::Commit()
695
0
{
696
    // remember the old text
697
0
    OUString sOld(GetEntryText());
698
699
    // do the reformat
700
0
    ReFormat();
701
702
    // did the text change?
703
0
    if (GetEntryText() != sOld)
704
0
    {   // consider the field as modified,
705
        // but we already have the most recent value;
706
        // don't reparse it from the text
707
        // (can lead to data loss when the format is lossy,
708
        //  as is e.g. our default date format: 2-digit year!)
709
0
        Modify(false);
710
0
    }
711
0
}
712
713
void Formatter::ReFormat()
714
0
{
715
0
    if (!IsEmptyFieldEnabled() || !GetEntryText().isEmpty())
716
0
    {
717
0
        if (TreatingAsNumber())
718
0
        {
719
0
            double dValue = GetValue();
720
0
            if ( m_bEnableNaN && std::isnan( dValue ) )
721
0
                return;
722
0
            ImplSetValue( dValue, true );
723
0
        }
724
0
        else
725
0
            SetTextFormatted(GetTextValue());
726
0
    }
727
0
}
728
729
void Formatter::SetMinValue(double dMin)
730
0
{
731
0
    DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMinValue : only to be used in numeric mode !");
732
733
0
    m_dMinValue = dMin;
734
0
    m_bHasMin = true;
735
    // for checking the current value at the new border -> ImplSetValue
736
0
    ReFormat();
737
0
}
738
739
void Formatter::SetMaxValue(double dMax)
740
0
{
741
0
    DBG_ASSERT(m_bTreatAsNumber, "FormattedField::SetMaxValue : only to be used in numeric mode !");
742
743
0
    m_dMaxValue = dMax;
744
0
    m_bHasMax = true;
745
    // for checking the current value at the new border -> ImplSetValue
746
0
    ReFormat();
747
0
}
748
749
void Formatter::SetTextValue(const OUString& rText)
750
0
{
751
0
    SetFieldText(rText, Selection(0, 0));
752
0
    ReFormat();
753
0
}
754
755
void Formatter::EnableEmptyField(bool bEnable)
756
0
{
757
0
    if (bEnable == m_bEnableEmptyField)
758
0
        return;
759
760
0
    m_bEnableEmptyField = bEnable;
761
0
    if (!m_bEnableEmptyField && GetEntryText().isEmpty())
762
0
        ImplSetValue(m_dCurrentValue, true);
763
0
}
764
765
void Formatter::ImplSetValue(double dVal, bool bForce)
766
0
{
767
0
    if (m_bHasMin && (dVal<m_dMinValue))
768
0
    {
769
0
        dVal = m_bWrapOnLimits ? fmod(dVal + m_dMaxValue + 1 - m_dMinValue, m_dMaxValue + 1) + m_dMinValue
770
0
                               : m_dMinValue;
771
0
    }
772
0
    if (m_bHasMax && (dVal>m_dMaxValue))
773
0
    {
774
0
        dVal = m_bWrapOnLimits ? fmod(dVal - m_dMinValue, m_dMaxValue + 1) + m_dMinValue
775
0
                               : m_dMaxValue;
776
0
    }
777
0
    if (!bForce && (dVal == GetValue()))
778
0
        return;
779
780
0
    m_ValueState = valueDouble;
781
0
    UpdateCurrentValue(dVal);
782
783
0
    const OUString sNewText = FormatValue(dVal);
784
0
    ImplSetTextImpl(sNewText, nullptr);
785
0
    DBG_ASSERT(CheckText(sNewText), "FormattedField::ImplSetValue : formatted string doesn't match the criteria !");
786
787
0
    m_ValueState = valueDouble;
788
0
}
789
790
std::optional<double> Formatter::ParseText(const OUString& rText)
791
0
{
792
0
    double fValue = 0.0;
793
0
    bool bUseExternalFormatterValue = false;
794
0
    if (m_aParseTextHdl.IsSet())
795
0
    {
796
0
        ParseResult aResult = m_aParseTextHdl.Call(rText);
797
0
        bUseExternalFormatterValue = aResult.m_eState != TRISTATE_INDET;
798
0
        if (bUseExternalFormatterValue)
799
0
        {
800
0
            if (aResult.m_eState == TRISTATE_TRUE)
801
0
                fValue = aResult.m_fValue;
802
0
            else
803
0
                fValue = m_dCurrentValue;
804
0
        }
805
0
    }
806
807
0
    if (!bUseExternalFormatterValue)
808
0
    {
809
0
        sal_uInt32 nFormatKey = m_nFormatKey; // IsNumberFormat changes the FormatKey!
810
811
0
        if (GetOrCreateFormatter().IsTextFormat(nFormatKey) && m_bTreatAsNumber)
812
            // for detection of values like "1,1" in fields that are formatted as text
813
0
            nFormatKey = 0;
814
815
        // special treatment for percentage formatting
816
0
        OUString sText = rText;
817
0
        if (GetOrCreateFormatter().GetType(m_nFormatKey) == SvNumFormatType::PERCENT)
818
0
        {
819
            // the language of our format
820
0
            const SvNumberformat* pFormatEntry = m_pFormatter->GetEntry(m_nFormatKey);
821
0
            assert(pFormatEntry && "due to GetType");
822
0
            LanguageType eLanguage = pFormatEntry->GetLanguage();
823
            // the default number format for this language
824
0
            sal_uLong nStandardNumericFormat = m_pFormatter->GetStandardFormat(SvNumFormatType::NUMBER, eLanguage);
825
826
0
            sal_uInt32 nTempFormat = nStandardNumericFormat;
827
0
            double dTemp;
828
0
            if (m_pFormatter->IsNumberFormat(sText, nTempFormat, dTemp) &&
829
0
                SvNumFormatType::NUMBER == m_pFormatter->GetType(nTempFormat))
830
                // the string is equivalent to a number formatted one (has no % sign) -> append it
831
0
                sText += "%";
832
            // (with this, an input of '3' becomes '3%', which then by the formatter is translated
833
            // into 0.03. Without this, the formatter would give us the double 3 for an input '3',
834
            // which equals 300 percent.
835
0
        }
836
0
        if (!GetOrCreateFormatter().IsNumberFormat(sText, nFormatKey, fValue))
837
0
            return std::optional<double>();
838
0
    }
839
840
0
    if (m_bHasMin && (fValue < m_dMinValue))
841
0
        fValue = m_dMinValue;
842
0
    if (m_bHasMax && (fValue > m_dMaxValue))
843
0
        fValue = m_dMaxValue;
844
0
    return std::optional<double>(fValue);
845
0
}
846
847
bool Formatter::ImplGetValue(double& dNewVal)
848
0
{
849
0
    dNewVal = m_dCurrentValue;
850
0
    if (m_ValueState == valueDouble)
851
0
        return true;
852
853
    // tdf#155241 default to m_dDefaultValue only if explicitly set
854
    // otherwise default to m_dCurrentValue
855
0
    if (m_bDefaultValueSet)
856
0
        dNewVal = m_dDefaultValue;
857
858
0
    OUString sText(GetEntryText());
859
0
    if (sText.isEmpty())
860
0
        return true;
861
862
0
    std::optional<double> aValue = ParseText(sText);
863
0
    if (!aValue.has_value())
864
0
        return false;
865
866
0
    dNewVal = aValue.value();
867
0
    return true;
868
0
}
869
870
void Formatter::SetValue(double dVal)
871
0
{
872
0
    ImplSetValue(dVal, m_ValueState != valueDouble);
873
0
}
874
875
double Formatter::GetValue()
876
0
{
877
0
    if ( !ImplGetValue( m_dCurrentValue ) )
878
0
        UpdateCurrentValue(m_bEnableNaN ? std::numeric_limits<double>::quiet_NaN() : m_dDefaultValue);
879
880
0
    m_ValueState = valueDouble;
881
0
    return m_dCurrentValue;
882
0
}
883
884
void Formatter::DisableRemainderFactor()
885
0
{
886
0
    m_bDisableRemainderFactor = true;
887
0
}
888
889
void Formatter::UseInputStringForFormatting()
890
0
{
891
0
    m_bUseInputStringForFormatting = true;
892
0
}
893
894
namespace
895
{
896
    class FieldFormatter : public Formatter
897
    {
898
    private:
899
        FormattedField& m_rSpinButton;
900
    public:
901
        FieldFormatter(FormattedField& rSpinButton)
902
0
            : m_rSpinButton(rSpinButton)
903
0
        {
904
0
        }
905
906
        // Formatter overrides
907
        virtual Selection GetEntrySelection() const override
908
0
        {
909
0
            return m_rSpinButton.GetSelection();
910
0
        }
911
912
        virtual OUString GetEntryText() const override
913
0
        {
914
0
            return m_rSpinButton.GetText();
915
0
        }
916
917
        void SetEntryText(const OUString& rText, const Selection& rSel) override
918
0
        {
919
0
            m_rSpinButton.SpinField::SetText(rText, rSel);
920
0
        }
921
922
        virtual void SetEntryTextColor(const ::Color* pColor) override
923
0
        {
924
0
            if (pColor)
925
0
                m_rSpinButton.SetControlForeground(*pColor);
926
0
            else
927
0
                m_rSpinButton.SetControlForeground();
928
0
        }
929
930
        virtual SelectionOptions GetEntrySelectionOptions() const override
931
0
        {
932
0
            return m_rSpinButton.GetSettings().GetStyleSettings().GetSelectionOptions();
933
0
        }
934
935
        virtual void FieldModified() override
936
0
        {
937
0
            m_rSpinButton.SpinField::Modify();
938
0
        }
939
940
        virtual void UpdateCurrentValue(double dCurrentValue) override
941
0
        {
942
0
            Formatter::UpdateCurrentValue(dCurrentValue);
943
0
            m_rSpinButton.SetUpperEnabled(!m_bHasMax || dCurrentValue < m_dMaxValue);
944
0
            m_rSpinButton.SetLowerEnabled(!m_bHasMin || dCurrentValue > m_dMinValue);
945
0
        }
946
    };
947
948
    class DoubleNumericFormatter : public FieldFormatter
949
    {
950
    private:
951
        DoubleNumericField& m_rNumericSpinButton;
952
    public:
953
        DoubleNumericFormatter(DoubleNumericField& rNumericSpinButton)
954
0
            : FieldFormatter(rNumericSpinButton)
955
0
            , m_rNumericSpinButton(rNumericSpinButton)
956
0
        {
957
0
        }
958
959
        virtual bool CheckText(const OUString& sText) const override
960
0
        {
961
            // We'd like to implement this using the NumberFormatter::IsNumberFormat, but unfortunately, this doesn't
962
            // recognize fragments of numbers (like, for instance "1e", which happens during entering e.g. "1e10")
963
            // Thus, the roundabout way via a regular expression
964
0
            return m_rNumericSpinButton.GetNumberValidator().isValidNumericFragment(sText);
965
0
        }
966
967
        virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
968
0
        {
969
0
            m_rNumericSpinButton.ResetConformanceTester();
970
0
            FieldFormatter::FormatChanged(nWhat);
971
0
        }
972
    };
973
974
    class DoubleCurrencyFormatter : public FieldFormatter
975
    {
976
    private:
977
        DoubleCurrencyField& m_rCurrencySpinButton;
978
        bool m_bChangingFormat;
979
    public:
980
        DoubleCurrencyFormatter(DoubleCurrencyField& rNumericSpinButton)
981
0
            : FieldFormatter(rNumericSpinButton)
982
0
            , m_rCurrencySpinButton(rNumericSpinButton)
983
0
            , m_bChangingFormat(false)
984
0
        {
985
0
        }
986
987
        virtual void FormatChanged(FORMAT_CHANGE_TYPE nWhat) override
988
0
        {
989
0
            if (m_bChangingFormat)
990
0
            {
991
0
                FieldFormatter::FormatChanged(nWhat);
992
0
                return;
993
0
            }
994
995
0
            switch (nWhat)
996
0
            {
997
0
                case FORMAT_CHANGE_TYPE::FORMATTER:
998
0
                case FORMAT_CHANGE_TYPE::PRECISION:
999
0
                case FORMAT_CHANGE_TYPE::THOUSANDSSEP:
1000
                    // the aspects which changed don't take our currency settings into account (in fact, they most probably
1001
                    // destroyed them)
1002
0
                    m_rCurrencySpinButton.UpdateCurrencyFormat();
1003
0
                    break;
1004
0
                case FORMAT_CHANGE_TYPE::KEYONLY:
1005
0
                    OSL_FAIL("DoubleCurrencyField::FormatChanged : somebody modified my key !");
1006
                    // We always build our own format from the settings we get via special methods (setCurrencySymbol etc.).
1007
                    // Nobody but ourself should modify the format key directly!
1008
0
                    break;
1009
0
                default: break;
1010
0
            }
1011
1012
0
            FieldFormatter::FormatChanged(nWhat);
1013
0
        }
1014
1015
        void GuardSetFormat(const OUString& rString, LanguageType eLanguage)
1016
0
        {
1017
            // set this new basic format
1018
0
            m_bChangingFormat = true;
1019
0
            SetFormat(rString, eLanguage);
1020
0
            m_bChangingFormat = false;
1021
0
        }
1022
1023
    };
1024
}
1025
1026
DoubleNumericField::DoubleNumericField(vcl::Window* pParent, WinBits nStyle)
1027
0
    : FormattedField(pParent, nStyle)
1028
0
{
1029
0
    m_xOwnFormatter.reset(new DoubleNumericFormatter(*this));
1030
0
    m_pFormatter = m_xOwnFormatter.get();
1031
0
    ResetConformanceTester();
1032
0
}
Unexecuted instantiation: DoubleNumericField::DoubleNumericField(vcl::Window*, long)
Unexecuted instantiation: DoubleNumericField::DoubleNumericField(vcl::Window*, long)
1033
1034
0
DoubleNumericField::~DoubleNumericField() = default;
1035
1036
void DoubleNumericField::ResetConformanceTester()
1037
0
{
1038
    // the thousands and the decimal separator are language dependent
1039
0
    Formatter& rFormatter = GetFormatter();
1040
0
    const SvNumberformat* pFormatEntry = rFormatter.GetOrCreateFormatter().GetEntry(rFormatter.GetFormatKey());
1041
1042
0
    sal_Unicode cSeparatorThousand = ',';
1043
0
    sal_Unicode cSeparatorDecimal = '.';
1044
0
    if (pFormatEntry)
1045
0
    {
1046
0
        LocaleDataWrapper aLocaleInfo( LanguageTag( pFormatEntry->GetLanguage()) );
1047
1048
0
        OUString sSeparator = aLocaleInfo.getNumThousandSep();
1049
0
        if (!sSeparator.isEmpty())
1050
0
            cSeparatorThousand = sSeparator[0];
1051
1052
0
        sSeparator = aLocaleInfo.getNumDecimalSep();
1053
0
        if (!sSeparator.isEmpty())
1054
0
            cSeparatorDecimal = sSeparator[0];
1055
0
    }
1056
1057
0
    m_pNumberValidator.reset(new validation::NumberValidator( cSeparatorThousand, cSeparatorDecimal ));
1058
0
}
1059
1060
1061
DoubleCurrencyField::DoubleCurrencyField(vcl::Window* pParent, WinBits nStyle)
1062
0
    :FormattedField(pParent, nStyle)
1063
0
{
1064
0
    m_xOwnFormatter.reset(new DoubleCurrencyFormatter(*this));
1065
0
    m_pFormatter = m_xOwnFormatter.get();
1066
1067
0
    m_bPrependCurrSym = false;
1068
1069
    // initialize with a system currency format
1070
0
    m_sCurrencySymbol = SvtSysLocale().GetLocaleData().getCurrSymbol();
1071
0
    UpdateCurrencyFormat();
1072
0
}
Unexecuted instantiation: DoubleCurrencyField::DoubleCurrencyField(vcl::Window*, long)
Unexecuted instantiation: DoubleCurrencyField::DoubleCurrencyField(vcl::Window*, long)
1073
1074
void DoubleCurrencyField::setCurrencySymbol(const OUString& rSymbol)
1075
0
{
1076
0
    if (m_sCurrencySymbol == rSymbol)
1077
0
        return;
1078
1079
0
    m_sCurrencySymbol = rSymbol;
1080
0
    UpdateCurrencyFormat();
1081
0
    m_pFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRENCY_SYMBOL);
1082
0
}
1083
1084
void DoubleCurrencyField::setPrependCurrSym(bool _bPrepend)
1085
0
{
1086
0
    if (m_bPrependCurrSym == _bPrepend)
1087
0
         return;
1088
1089
0
    m_bPrependCurrSym = _bPrepend;
1090
0
    UpdateCurrencyFormat();
1091
0
    m_pFormatter->FormatChanged(FORMAT_CHANGE_TYPE::CURRSYM_POSITION);
1092
0
}
1093
1094
void DoubleCurrencyField::UpdateCurrencyFormat()
1095
0
{
1096
    // the old settings
1097
0
    LanguageType eLanguage;
1098
0
    m_pFormatter->GetFormat(eLanguage);
1099
0
    bool bThSep = m_pFormatter->GetThousandsSep();
1100
0
    sal_uInt16 nDigits = m_pFormatter->GetDecimalDigits();
1101
1102
    // build a new format string with the base class' and my own settings
1103
1104
    /* Strangely with gcc 4.6.3 this needs a temporary LanguageTag, otherwise
1105
     * there's
1106
     * error: request for member 'getNumThousandSep' in 'aLocaleInfo', which is
1107
     * of non-class type 'LocaleDataWrapper(LanguageTag)' */
1108
0
    LocaleDataWrapper aLocaleInfo(( LanguageTag(eLanguage) ));
1109
1110
0
    OUStringBuffer sNewFormat;
1111
0
    if (bThSep)
1112
0
    {
1113
0
        sNewFormat.append("#" + aLocaleInfo.getNumThousandSep() + "##0");
1114
0
    }
1115
0
    else
1116
0
        sNewFormat.append('0');
1117
1118
0
    if (nDigits)
1119
0
    {
1120
0
        sNewFormat.append(aLocaleInfo.getNumDecimalSep() + RepeatedUChar('0', nDigits));
1121
0
    }
1122
1123
0
    if (getPrependCurrSym())
1124
0
    {
1125
0
        OUString sSymbol = getCurrencySymbol();
1126
0
        sSymbol = comphelper::string::strip(sSymbol, ' ');
1127
1128
0
        OUString sTemp =
1129
0
            "[$" + sSymbol + "] "
1130
0
            + sNewFormat
1131
        // for negative values : $ -0.00, not -$ 0.00...
1132
        // (the real solution would be a possibility to choose a "positive currency format" and a "negative currency format"...
1133
        // But not now... (and hey, you could take a formatted field for this...))
1134
        // FS - 31.03.00 74642
1135
0
            + ";[$"
1136
0
            + sSymbol
1137
0
            + "] -"
1138
0
            + sNewFormat;
1139
1140
0
        sNewFormat = sTemp;
1141
0
    }
1142
0
    else
1143
0
    {
1144
0
        OUString sTemp = getCurrencySymbol();
1145
0
        sTemp = comphelper::string::strip(sTemp, ' ');
1146
1147
0
        sNewFormat.append(" [$" + sTemp + "]");
1148
0
    }
1149
1150
    // set this new basic format
1151
0
    static_cast<DoubleCurrencyFormatter*>(m_pFormatter)->GuardSetFormat(sNewFormat.makeStringAndClear(), eLanguage);
1152
0
}
1153
1154
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */