Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/control/field.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 <sal/config.h>
21
22
#include <osl/diagnose.hxx>
23
#include <comphelper/string.hxx>
24
#include <tools/json_writer.hxx>
25
#include <unotools/localedatawrapper.hxx>
26
#include <o3tl/string_view.hxx>
27
#include <i18nutil/unicode.hxx>
28
29
#include <vcl/event.hxx>
30
#include <vcl/fieldvalues.hxx>
31
#include <vcl/toolkit/field.hxx>
32
#include <vcl/uitest/metricfielduiobject.hxx>
33
34
#include <svdata.hxx>
35
36
#include <boost/property_tree/ptree.hpp>
37
38
#include <cmath>
39
#include <string_view>
40
41
namespace
42
{
43
44
std::string FieldUnitToString(FieldUnit unit)
45
0
{
46
0
    switch(unit)
47
0
    {
48
0
        case FieldUnit::NONE:
49
0
            return "";
50
51
0
        case FieldUnit::MM:
52
0
            return "mm";
53
54
0
        case FieldUnit::CM:
55
0
            return "cm";
56
57
0
        case FieldUnit::M:
58
0
            return "m";
59
60
0
        case FieldUnit::KM:
61
0
            return "km";
62
63
0
        case FieldUnit::TWIP:
64
0
            return "twip";
65
66
0
        case FieldUnit::POINT:
67
0
            return "point";
68
69
0
        case FieldUnit::PICA:
70
0
            return "pica";
71
72
0
        case FieldUnit::INCH:
73
0
            return "inch";
74
75
0
        case FieldUnit::FOOT:
76
0
            return "foot";
77
78
0
        case FieldUnit::MILE:
79
0
            return "mile";
80
81
0
        case FieldUnit::CHAR:
82
0
            return "char";
83
84
0
        case FieldUnit::LINE:
85
0
            return "line";
86
87
0
        case FieldUnit::CUSTOM:
88
0
            return "custom";
89
90
0
        case FieldUnit::PERCENT:
91
0
            return "percent";
92
93
0
        case FieldUnit::MM_100TH:
94
0
            return "mm100th";
95
96
0
        case FieldUnit::PIXEL:
97
0
            return "pixel";
98
99
0
        case FieldUnit::DEGREE:
100
0
            return "degree";
101
102
0
        case FieldUnit::SECOND:
103
0
            return "second";
104
105
0
        case FieldUnit::MILLISECOND:
106
0
            return "millisecond";
107
108
0
        case FieldUnit::FONT_EM:
109
0
            return "em";
110
111
0
        case FieldUnit::FONT_CJK_ADVANCE:
112
0
            return "ic";
113
0
    }
114
115
0
    return "";
116
0
}
117
118
sal_Int64 ImplPower10( sal_uInt16 n )
119
0
{
120
0
    sal_Int64 nValue = 1;
121
122
0
    for (sal_uInt16 i = 0; i < n; i++)
123
0
    {
124
0
        nValue *= 10;
125
0
    }
126
127
0
    return nValue;
128
0
}
129
130
bool ImplNumericProcessKeyInput( const KeyEvent& rKEvt,
131
                                 bool bStrictFormat, bool bThousandSep,
132
                                 const LocaleDataWrapper& rLocaleDataWrapper )
133
0
{
134
0
    if ( !bStrictFormat )
135
0
        return false;
136
0
    else
137
0
    {
138
0
        sal_Unicode cChar = rKEvt.GetCharCode();
139
0
        sal_uInt16      nGroup = rKEvt.GetKeyCode().GetGroup();
140
141
0
        return !((nGroup == KEYGROUP_FKEYS) ||
142
0
                 (nGroup == KEYGROUP_CURSOR) ||
143
0
                 (nGroup == KEYGROUP_MISC) ||
144
0
                 ((cChar >= '0') && (cChar <= '9')) ||
145
0
                 rLocaleDataWrapper.getNumDecimalSep() == OUStringChar(cChar) ||
146
0
                 (bThousandSep && rLocaleDataWrapper.getNumThousandSep() == OUStringChar(cChar)) ||
147
0
                 rLocaleDataWrapper.getNumDecimalSepAlt() == OUStringChar(cChar) ||
148
0
                 (cChar == '-'));
149
0
    }
150
0
}
151
152
// Takes a string with a number, which may be an integer, a floating-point with locale-specified
153
// decimal separator, or a fraction (and if allowed, where negatives can be represented in currency
154
// format - in parentheses); pre-processes the string to be a floating-point scaled by nDecDigits;
155
// returns a pair { scaled_whole_part_string, decimal_part_string }.
156
std::pair<OUString, OUString> ToScaledWholeAndDec(std::u16string_view aStr, sal_uInt16 nDecDigits,
157
                                                  const LocaleDataWrapper& rLocaleDataWrapper,
158
                                                  bool bCurrency)
159
0
{
160
    // remove leading and trailing spaces
161
0
    aStr = o3tl::trim(aStr);
162
0
    OUStringBuffer      aStr1, aStr2, aStrNum, aStrDenom;
163
0
    bool                bNegative = false;
164
0
    bool                bFrac = false;
165
166
    // react on empty string
167
0
    if (aStr.empty())
168
0
        return {};
169
170
    // find position of decimal point
171
0
    auto nDecPos = aStr.find(rLocaleDataWrapper.getNumDecimalSep());
172
0
    if (nDecPos == std::u16string_view::npos && !rLocaleDataWrapper.getNumDecimalSepAlt().isEmpty())
173
0
        nDecPos = aStr.find( rLocaleDataWrapper.getNumDecimalSepAlt() );
174
175
    // parse fractional strings
176
0
    if (auto nFracDivPos = aStr.find('/');
177
0
        nFracDivPos > 0 && nFracDivPos != std::u16string_view::npos)
178
0
    {
179
0
        bFrac = true;
180
0
        auto nFracNumPos = aStr.rfind(' ', nFracDivPos);
181
182
        // If in "a b/c" format.
183
0
        if (nFracNumPos != std::u16string_view::npos)
184
0
        {
185
0
            aStr1.append(aStr.substr(0, nFracNumPos));
186
0
            aStrNum.append(aStr.substr(nFracNumPos+1, nFracDivPos-nFracNumPos-1));
187
0
            aStrDenom.append(aStr.substr(nFracDivPos+1));
188
0
        }
189
        // "a/b" format, or not a fraction at all
190
0
        else
191
0
        {
192
0
            aStrNum.append(aStr.substr(0, nFracDivPos));
193
0
            aStrDenom.append(aStr.substr(nFracDivPos+1));
194
0
        }
195
196
0
    }
197
    // parse decimal strings
198
0
    else if (nDecPos != std::u16string_view::npos)
199
0
    {
200
0
        aStr1.append(aStr.substr(0, nDecPos));
201
0
        aStr2.append(aStr.substr(nDecPos+1));
202
0
    }
203
0
    else
204
0
        aStr1 = aStr;
205
206
    // negative?
207
0
    if ( bCurrency )
208
0
    {
209
0
        if ( aStr.starts_with('(') && aStr.ends_with(')') )
210
0
            bNegative = true;
211
0
        if ( !bNegative )
212
0
        {
213
0
            for (size_t i = 0; i < aStr.size(); i++)
214
0
            {
215
0
                if ( (aStr[i] >= '0') && (aStr[i] <= '9') )
216
0
                    break;
217
0
                else if ( aStr[i] == '-' )
218
0
                {
219
0
                    bNegative = true;
220
0
                    break;
221
0
                }
222
0
            }
223
0
        }
224
0
        if (!bNegative && !aStr.empty())
225
0
        {
226
0
            sal_uInt16 nFormat = rLocaleDataWrapper.getCurrNegativeFormat();
227
0
            if ( (nFormat == 3) || (nFormat == 6)  || // $1- || 1-$
228
0
                 (nFormat == 7) || (nFormat == 10) )  // 1$- || 1 $-
229
0
            {
230
0
                for (size_t i = aStr.size() - 1; i > 0; --i)
231
0
                {
232
0
                    if ( (aStr[i] >= '0') && (aStr[i] <= '9') )
233
0
                        break;
234
0
                    else if ( aStr[i] == '-' )
235
0
                    {
236
0
                        bNegative = true;
237
0
                        break;
238
0
                    }
239
0
                }
240
0
            }
241
0
        }
242
0
    }
243
0
    else
244
0
    {
245
0
        if ( !aStr1.isEmpty() && aStr1[0] == '-')
246
0
            bNegative = true;
247
0
        if ( !aStrNum.isEmpty() && aStrNum[0] == '-') // For non-mixed fractions
248
0
            bNegative = true;
249
0
    }
250
251
    // remove all unwanted characters
252
    // For whole number
253
0
    for (sal_Int32 i=0; i < aStr1.getLength(); )
254
0
    {
255
0
        if ( (aStr1[i] >= '0') && (aStr1[i] <= '9') )
256
0
            i++;
257
0
        else
258
0
            aStr1.remove( i, 1 );
259
0
    }
260
    // For decimal
261
0
    if (!bFrac) {
262
0
        for (sal_Int32 i=0; i < aStr2.getLength(); )
263
0
        {
264
0
            if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
265
0
                ++i;
266
0
            else
267
0
                aStr2.remove(i, 1);
268
0
        }
269
0
    }
270
0
    else {
271
        // for numerator
272
0
        for (sal_Int32 i=0; i < aStrNum.getLength(); )
273
0
        {
274
0
            if ((aStrNum[i] >= '0') && (aStrNum[i] <= '9'))
275
0
                ++i;
276
0
            else
277
0
                aStrNum.remove(i, 1);
278
0
        }
279
        // for denominator
280
0
        for (sal_Int32 i=0; i < aStrDenom.getLength(); )
281
0
        {
282
0
            if ((aStrDenom[i] >= '0') && (aStrDenom[i] <= '9'))
283
0
                ++i;
284
0
            else
285
0
                aStrDenom.remove(i, 1);
286
0
        }
287
0
    }
288
289
290
0
    if ( !bFrac && aStr1.isEmpty() && aStr2.isEmpty() )
291
0
        return {};
292
0
    else if ( bFrac && aStr1.isEmpty() && (aStrNum.isEmpty() || aStrDenom.isEmpty()) )
293
0
        return {};
294
295
0
    if ( aStr1.isEmpty() )
296
0
        aStr1 = "0";
297
0
    if ( bNegative )
298
0
        aStr1.insert(0, "-");
299
300
    // Convert fractional strings
301
0
    if (bFrac) {
302
        // Convert to fraction
303
0
        sal_Int64 nWholeNum = o3tl::toInt64(aStr1);
304
0
        aStr1.setLength(0);
305
0
        sal_Int64 nNum = o3tl::toInt64(aStrNum);
306
0
        sal_Int64 nDenom = o3tl::toInt64(aStrDenom);
307
0
        if (nDenom == 0) return {}; // Division by zero
308
0
        double nFrac2Dec = nWholeNum + static_cast<double>(nNum)/nDenom; // Convert to double for floating point precision
309
0
        OUStringBuffer aStrFrac(OUString::number(nFrac2Dec));
310
        // Reconvert division result to string and parse
311
0
        nDecPos = aStrFrac.indexOf('.');
312
0
        if (nDecPos != std::u16string_view::npos)
313
0
        {
314
0
            aStr1.append(aStrFrac.getStr(), nDecPos);
315
0
            aStr2.append(aStrFrac.getStr()+nDecPos+1);
316
0
        }
317
0
        else
318
0
            aStr1 = std::move(aStrFrac);
319
0
    }
320
321
0
    if (nDecDigits)
322
0
    {
323
0
        const sal_Int32 moveTo1 = std::min(static_cast<sal_Int32>(nDecDigits), aStr2.getLength());
324
0
        aStr1.append(aStr2.subView(0, moveTo1) + RepeatedUChar('0', nDecDigits - moveTo1));
325
0
        aStr2.remove(0, moveTo1);
326
0
    }
327
328
0
    return { aStr1.makeStringAndClear(), aStr2.makeStringAndClear() };
329
0
}
330
331
bool ImplNumericGetValue(std::u16string_view rStr, sal_Int64& rValue, sal_uInt16 nDecDigits,
332
                         const LocaleDataWrapper& rLocaleDataWrapper, bool bCurrency = false)
333
0
{
334
0
    const auto [whole, dec] = ToScaledWholeAndDec(rStr, nDecDigits, rLocaleDataWrapper, bCurrency);
335
0
    if (whole.isEmpty() && dec.isEmpty())
336
0
        return false;
337
338
    // prune and round fraction
339
0
    const bool bRound = !dec.isEmpty() && dec[0] >= '5';
340
341
    // check range
342
0
    sal_Int64 nValue = whole.toInt64();
343
0
    const bool bNegative = whole.startsWith("-");
344
0
    if( nValue == 0 )
345
0
    {
346
        // check if string is equivalent to zero
347
0
        sal_Int32 nIndex = bNegative ? 1 : 0;
348
0
        while (nIndex < whole.getLength() && whole[nIndex] == '0')
349
0
            ++nIndex;
350
0
        if (nIndex < whole.getLength())
351
0
        {
352
0
            rValue = bNegative ? SAL_MIN_INT64 : SAL_MAX_INT64;
353
0
            return true;
354
0
        }
355
0
    }
356
0
    if (bRound)
357
0
    {
358
0
        if ( !bNegative )
359
0
            nValue++;
360
0
        else
361
0
            nValue--;
362
0
    }
363
364
0
    rValue = nValue;
365
366
0
    return true;
367
0
}
368
369
// The returned double is scaled according to nDecDigits, same way as ImplNumericGetValue
370
bool ImplNumericGetDoubleValue(std::u16string_view rStr, double& rValue, sal_uInt16 nDecDigits,
371
                               const LocaleDataWrapper& rLocaleDataWrapper)
372
0
{
373
0
    const auto [whole, dec] = ToScaledWholeAndDec(rStr, nDecDigits, rLocaleDataWrapper, false);
374
0
    if (whole.isEmpty() && dec.isEmpty())
375
0
        return false;
376
377
0
    rValue = o3tl::toDouble(Concat2View(whole + "." + dec));
378
0
    return true;
379
0
}
380
381
void ImplUpdateSeparatorString( OUString& io_rText,
382
                                       std::u16string_view rOldDecSep, std::u16string_view rNewDecSep,
383
                                       std::u16string_view rOldThSep, std::u16string_view rNewThSep )
384
0
{
385
0
    OUStringBuffer aBuf( io_rText.getLength() );
386
0
    sal_Int32 nIndexDec = 0, nIndexTh = 0, nIndex = 0;
387
388
0
    const sal_Unicode* pBuffer = io_rText.getStr();
389
0
    while( nIndex != -1 )
390
0
    {
391
0
        nIndexDec = io_rText.indexOf( rOldDecSep, nIndex );
392
0
        nIndexTh = io_rText.indexOf( rOldThSep, nIndex );
393
0
        if(   (nIndexTh != -1 && nIndexDec != -1 && nIndexTh < nIndexDec )
394
0
           || (nIndexTh != -1 && nIndexDec == -1)
395
0
           )
396
0
        {
397
0
            aBuf.append( OUString::Concat(std::u16string_view(pBuffer + nIndex, nIndexTh - nIndex )) + rNewThSep );
398
0
            nIndex = nIndexTh + rOldThSep.size();
399
0
        }
400
0
        else if( nIndexDec != -1 )
401
0
        {
402
0
            aBuf.append( OUString::Concat(std::u16string_view(pBuffer + nIndex, nIndexDec - nIndex )) + rNewDecSep );
403
0
            nIndex = nIndexDec + rOldDecSep.size();
404
0
        }
405
0
        else
406
0
        {
407
0
            aBuf.append( pBuffer + nIndex );
408
0
            nIndex = -1;
409
0
        }
410
0
    }
411
412
0
    io_rText = aBuf.makeStringAndClear();
413
0
}
414
415
void ImplUpdateSeparators( std::u16string_view rOldDecSep, std::u16string_view rNewDecSep,
416
                                  std::u16string_view rOldThSep, std::u16string_view rNewThSep,
417
                                  Edit* pEdit )
418
0
{
419
0
    bool bChangeDec = (rOldDecSep != rNewDecSep);
420
0
    bool bChangeTh = (rOldThSep != rNewThSep );
421
422
0
    if( !(bChangeDec || bChangeTh) )
423
0
        return;
424
425
0
    bool bUpdateMode = pEdit->IsUpdateMode();
426
0
    pEdit->SetUpdateMode( false );
427
0
    OUString aText = pEdit->GetText();
428
0
    ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep );
429
0
    pEdit->SetText( aText );
430
431
0
    ComboBox* pCombo = dynamic_cast<ComboBox*>(pEdit);
432
0
    if( pCombo )
433
0
    {
434
        // update box entries
435
0
        sal_Int32 nEntryCount = pCombo->GetEntryCount();
436
0
        for ( sal_Int32 i=0; i < nEntryCount; i++ )
437
0
        {
438
0
            aText = pCombo->GetEntry( i );
439
0
            void* pEntryData = pCombo->GetEntryData( i );
440
0
            ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep );
441
0
            pCombo->RemoveEntryAt(i);
442
0
            pCombo->InsertEntry( aText, i );
443
0
            pCombo->SetEntryData( i, pEntryData );
444
0
        }
445
0
    }
446
0
    if( bUpdateMode )
447
0
        pEdit->SetUpdateMode( bUpdateMode );
448
0
}
449
450
sal_Int64 clipDoubleAgainstMinMax(double n, sal_Int64 nMin, sal_Int64 nMax)
451
0
{
452
    // caution: precision loss in double cast
453
0
    if (n <= static_cast<double>(nMin))
454
0
        return nMin;
455
0
    else if (n >= static_cast<double>(nMax))
456
0
        return nMax;
457
0
    else
458
0
        return static_cast<sal_Int64>(std::round(n));
459
0
}
460
461
} // namespace
462
463
FormatterBase::FormatterBase(Edit* pField)
464
0
{
465
0
    mpField                     = pField;
466
0
    mpLocaleDataWrapper         = nullptr;
467
0
    mbReformat                  = false;
468
0
    mbStrictFormat              = false;
469
0
    mbEmptyFieldValue           = false;
470
0
    mbEmptyFieldValueEnabled    = false;
471
0
}
472
473
FormatterBase::~FormatterBase()
474
0
{
475
0
}
476
477
LocaleDataWrapper& FormatterBase::ImplGetLocaleDataWrapper() const
478
0
{
479
0
    if ( !mpLocaleDataWrapper )
480
0
    {
481
0
        mpLocaleDataWrapper.reset( new LocaleDataWrapper( GetLanguageTag() ) );
482
0
    }
483
0
    return *mpLocaleDataWrapper;
484
0
}
485
486
/** reset the LocaleDataWrapper when the language tag changes */
487
void FormatterBase::ImplResetLocaleDataWrapper() const
488
0
{
489
    // just get rid of, the next time it is requested, it will get loaded with the right
490
    // language tag
491
0
    mpLocaleDataWrapper.reset();
492
0
}
493
494
const LocaleDataWrapper& FormatterBase::GetLocaleDataWrapper() const
495
0
{
496
0
    return ImplGetLocaleDataWrapper();
497
0
}
498
499
void FormatterBase::Reformat()
500
0
{
501
0
}
502
503
void FormatterBase::ReformatAll()
504
0
{
505
0
    Reformat();
506
0
};
507
508
void FormatterBase::SetStrictFormat( bool bStrict )
509
0
{
510
0
    if ( bStrict != mbStrictFormat )
511
0
    {
512
0
        mbStrictFormat = bStrict;
513
0
        if ( mbStrictFormat )
514
0
            ReformatAll();
515
0
    }
516
0
}
517
518
const css::lang::Locale& FormatterBase::GetLocale() const
519
0
{
520
0
    if ( mpField )
521
0
        return mpField->GetSettings().GetLanguageTag().getLocale();
522
0
    else
523
0
        return Application::GetSettings().GetLanguageTag().getLocale();
524
0
}
525
526
const LanguageTag& FormatterBase::GetLanguageTag() const
527
0
{
528
0
    if ( mpField )
529
0
        return mpField->GetSettings().GetLanguageTag();
530
0
    else
531
0
        return Application::GetSettings().GetLanguageTag();
532
0
}
533
534
void FormatterBase::ImplSetText( const OUString& rText, Selection const * pNewSelection )
535
0
{
536
0
    if ( mpField )
537
0
    {
538
0
        if (pNewSelection)
539
0
            mpField->SetText(rText, *pNewSelection);
540
0
        else
541
0
        {
542
0
            Selection aSel = mpField->GetSelection();
543
0
            aSel.Min() = aSel.Max();
544
0
            mpField->SetText(rText, aSel);
545
0
        }
546
0
        MarkToBeReformatted( false );
547
0
    }
548
0
}
549
550
void FormatterBase::SetEmptyFieldValue()
551
0
{
552
0
    if ( mpField )
553
0
        mpField->SetText( OUString() );
554
0
    mbEmptyFieldValue = true;
555
0
}
556
557
bool FormatterBase::IsEmptyFieldValue() const
558
0
{
559
0
    return (!mpField || mpField->GetText().isEmpty());
560
0
}
561
562
void NumericFormatter::FormatValue(Selection const * pNewSelection)
563
0
{
564
0
    mbFormatting = true;
565
0
    ImplSetText(CreateFieldText(mnLastValue), pNewSelection);
566
0
    mbFormatting = false;
567
0
}
568
569
void NumericFormatter::ImplNumericReformat()
570
0
{
571
0
    mnLastValue = GetValue();
572
0
    FormatValue();
573
0
}
574
575
NumericFormatter::NumericFormatter(Edit* pEdit)
576
0
    : FormatterBase(pEdit)
577
0
    , mnLastValue(0)
578
0
    , mnMin(0)
579
    // a "large" value substantially smaller than SAL_MAX_INT64, to avoid
580
    // overflow in computations using this "dummy" value
581
0
    , mnMax(SAL_MAX_INT32)
582
0
    , mbFormatting(false)
583
0
    , mnSpinSize(1)
584
    // for fields
585
0
    , mnFirst(mnMin)
586
0
    , mnLast(mnMax)
587
0
    , mnDecimalDigits(0)
588
0
    , mbThousandSep(true)
589
0
{
590
0
    ReformatAll();
591
0
}
592
593
NumericFormatter::~NumericFormatter()
594
{
595
}
596
597
void NumericFormatter::SetMin( sal_Int64 nNewMin )
598
0
{
599
0
    mnMin = nNewMin;
600
0
    if ( !IsEmptyFieldValue() )
601
0
        ReformatAll();
602
0
}
603
604
void NumericFormatter::SetMax( sal_Int64 nNewMax )
605
0
{
606
0
    mnMax = nNewMax;
607
0
    if ( !IsEmptyFieldValue() )
608
0
        ReformatAll();
609
0
}
610
611
void NumericFormatter::SetUseThousandSep( bool bValue )
612
0
{
613
0
    mbThousandSep = bValue;
614
0
    ReformatAll();
615
0
}
616
617
void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits )
618
0
{
619
0
    mnDecimalDigits = nDigits;
620
0
    ReformatAll();
621
0
}
622
623
void NumericFormatter::SetValue( sal_Int64 nNewValue )
624
0
{
625
0
    SetUserValue( nNewValue );
626
0
    SetEmptyFieldValueData( false );
627
0
}
628
629
OUString NumericFormatter::CreateFieldText( sal_Int64 nValue ) const
630
0
{
631
0
    return ImplGetLocaleDataWrapper().getNum( nValue, GetDecimalDigits(), IsUseThousandSep(), /*ShowTrailingZeros*/true );
632
0
}
633
634
void NumericFormatter::ImplSetUserValue( sal_Int64 nNewValue, Selection const * pNewSelection )
635
0
{
636
0
    nNewValue = ClipAgainstMinMax(nNewValue);
637
0
    mnLastValue = nNewValue;
638
639
0
    if ( GetField() )
640
0
        FormatValue(pNewSelection);
641
0
}
642
643
void NumericFormatter::SetUserValue( sal_Int64 nNewValue )
644
0
{
645
0
    ImplSetUserValue( nNewValue );
646
0
}
647
648
sal_Int64 NumericFormatter::GetValueFromString(std::u16string_view rStr) const
649
0
{
650
0
    sal_Int64 nTempValue;
651
652
0
    if (ImplNumericGetValue(rStr, nTempValue,
653
0
        GetDecimalDigits(), ImplGetLocaleDataWrapper()))
654
0
    {
655
0
        return ClipAgainstMinMax(nTempValue);
656
0
    }
657
0
    else
658
0
        return mnLastValue;
659
0
}
660
661
OUString NumericFormatter::GetValueString() const
662
0
{
663
0
    return Application::GetSettings().GetNeutralLocaleDataWrapper().
664
0
        getNum(GetValue(), GetDecimalDigits(), false, false);
665
0
}
666
667
// currently used by online
668
void NumericFormatter::SetValueFromString(const OUString& rStr)
669
0
{
670
0
    sal_Int64 nValue;
671
672
0
    if (ImplNumericGetValue(rStr, nValue, GetDecimalDigits(),
673
0
        Application::GetSettings().GetNeutralLocaleDataWrapper()))
674
0
    {
675
0
        ImplNewFieldValue(nValue);
676
0
    }
677
0
    else
678
0
    {
679
0
        SAL_WARN("vcl", "fail to convert the value: " << rStr );
680
0
    }
681
0
}
682
683
sal_Int64 NumericFormatter::GetValue() const
684
0
{
685
0
    if (mbFormatting) //don't parse the entry if we're currently formatting what to put in it
686
0
        return mnLastValue;
687
688
0
    return GetField() ? GetValueFromString(GetField()->GetText()) : 0;
689
0
}
690
691
sal_Int64 NumericFormatter::Normalize( sal_Int64 nValue ) const
692
0
{
693
0
    return (nValue * ImplPower10( GetDecimalDigits() ) );
694
0
}
695
696
sal_Int64 NumericFormatter::Denormalize( sal_Int64 nValue ) const
697
0
{
698
0
    sal_Int64 nFactor = ImplPower10( GetDecimalDigits() );
699
700
0
    if ((nValue < ( SAL_MIN_INT64 + nFactor )) ||
701
0
        (nValue > ( SAL_MAX_INT64 - nFactor )))
702
0
    {
703
0
        return ( nValue / nFactor );
704
0
    }
705
706
0
    if( nValue < 0 )
707
0
    {
708
0
        sal_Int64 nHalf = nFactor / 2;
709
0
        return ((nValue - nHalf) / nFactor );
710
0
    }
711
0
    else
712
0
    {
713
0
        sal_Int64 nHalf = nFactor / 2;
714
0
        return ((nValue + nHalf) / nFactor );
715
0
    }
716
0
}
717
718
void NumericFormatter::Reformat()
719
0
{
720
0
    if ( !GetField() )
721
0
        return;
722
723
0
    if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
724
0
        return;
725
726
0
    ImplNumericReformat();
727
0
}
728
729
void NumericFormatter::FieldUp()
730
0
{
731
0
    sal_Int64 nValue = GetValue();
732
0
    sal_Int64 nRemainder = nValue % mnSpinSize;
733
0
    if (nValue >= 0)
734
0
        nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue + mnSpinSize - nRemainder;
735
0
    else
736
0
        nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue - nRemainder;
737
738
0
    nValue = ClipAgainstMinMax(nValue);
739
740
0
    ImplNewFieldValue( nValue );
741
0
}
742
743
void NumericFormatter::FieldDown()
744
0
{
745
0
    sal_Int64 nValue = GetValue();
746
0
    sal_Int64 nRemainder = nValue % mnSpinSize;
747
0
    if (nValue >= 0)
748
0
        nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - nRemainder;
749
0
    else
750
0
        nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - mnSpinSize - nRemainder;
751
752
0
    nValue = ClipAgainstMinMax(nValue);
753
754
0
    ImplNewFieldValue( nValue );
755
0
}
756
757
void NumericFormatter::FieldFirst()
758
0
{
759
0
    ImplNewFieldValue( mnFirst );
760
0
}
761
762
void NumericFormatter::FieldLast()
763
0
{
764
0
    ImplNewFieldValue( mnLast );
765
0
}
766
767
void NumericFormatter::ImplNewFieldValue( sal_Int64 nNewValue )
768
0
{
769
0
    if ( !GetField() )
770
0
        return;
771
772
    // !!! We should check why we do not validate in ImplSetUserValue() if the value was
773
    // changed. This should be done there as well since otherwise the call to Modify would not
774
    // be allowed. Anyway, the paths from ImplNewFieldValue, ImplSetUserValue, and ImplSetText
775
    // should be checked and clearly traced (with comment) in order to find out what happens.
776
777
0
    Selection aSelection = GetField()->GetSelection();
778
0
    aSelection.Normalize();
779
0
    OUString aText = GetField()->GetText();
780
    // leave it as is if selected until end
781
0
    if ( static_cast<sal_Int32>(aSelection.Max()) == aText.getLength() )
782
0
    {
783
0
        if ( !aSelection.Len() )
784
0
            aSelection.Min() = SELECTION_MAX;
785
0
        aSelection.Max() = SELECTION_MAX;
786
0
    }
787
788
0
    sal_Int64 nOldLastValue  = mnLastValue;
789
0
    ImplSetUserValue( nNewValue, &aSelection );
790
0
    mnLastValue = nOldLastValue;
791
792
    // Modify during Edit is only set during KeyInput
793
0
    if ( GetField()->GetText() != aText )
794
0
    {
795
0
        GetField()->SetModifyFlag();
796
0
        GetField()->Modify();
797
0
    }
798
0
}
799
800
sal_Int64 NumericFormatter::ClipAgainstMinMax(sal_Int64 nValue) const
801
0
{
802
0
    return std::clamp(nValue, GetMin(), GetMax());
803
0
}
804
805
sal_Int64 NumericFormatter::ClipDoubleAgainstMinMax(double nValue) const
806
0
{
807
0
    return clipDoubleAgainstMinMax(nValue, GetMin(), GetMax());
808
0
}
809
810
namespace
811
{
812
    Size calcMinimumSize(const Edit &rSpinField, const NumericFormatter &rFormatter)
813
0
    {
814
0
        OUStringBuffer aBuf;
815
0
        sal_Int32 nTextLen;
816
817
0
        nTextLen = OUString::number(rFormatter.GetMin()).length;
818
0
        comphelper::string::padToLength(aBuf, nTextLen, '9');
819
0
        Size aMinTextSize = rSpinField.CalcMinimumSizeForText(
820
0
            rFormatter.CreateFieldText(OUString::unacquired(aBuf).toInt64()));
821
0
        aBuf.setLength(0);
822
823
0
        nTextLen = OUString::number(rFormatter.GetMax()).length;
824
0
        comphelper::string::padToLength(aBuf, nTextLen, '9');
825
0
        Size aMaxTextSize = rSpinField.CalcMinimumSizeForText(
826
0
            rFormatter.CreateFieldText(OUString::unacquired(aBuf).toInt64()));
827
0
        aBuf.setLength(0);
828
829
0
        Size aRet(std::max(aMinTextSize.Width(), aMaxTextSize.Width()),
830
0
                  std::max(aMinTextSize.Height(), aMaxTextSize.Height()));
831
832
0
        aBuf = "999999999";
833
0
        sal_uInt16 nDigits = rFormatter.GetDecimalDigits();
834
0
        if (nDigits)
835
0
        {
836
0
            aBuf.append("." + RepeatedUChar('9', nDigits));
837
0
        }
838
0
        aMaxTextSize = rSpinField.CalcMinimumSizeForText(aBuf.makeStringAndClear());
839
0
        aRet.setWidth( std::min(aRet.Width(), aMaxTextSize.Width()) );
840
841
0
        return aRet;
842
0
    }
843
}
844
845
NumericBox::NumericBox(vcl::Window* pParent, WinBits nWinStyle)
846
0
    : ComboBox(pParent, nWinStyle)
847
0
    , NumericFormatter(this)
848
0
{
849
0
    Reformat();
850
0
    if ( !(nWinStyle & WB_HIDE ) )
851
0
        Show();
852
0
}
Unexecuted instantiation: NumericBox::NumericBox(vcl::Window*, long)
Unexecuted instantiation: NumericBox::NumericBox(vcl::Window*, long)
853
854
void NumericBox::dispose()
855
0
{
856
0
    ClearField();
857
0
    ComboBox::dispose();
858
0
}
859
860
Size NumericBox::CalcMinimumSize() const
861
0
{
862
0
    Size aRet(calcMinimumSize(*this, *this));
863
864
0
    if (IsDropDownBox())
865
0
    {
866
0
        Size aComboSugg(ComboBox::CalcMinimumSize());
867
0
        aRet.setWidth( std::max(aRet.Width(), aComboSugg.Width()) );
868
0
        aRet.setHeight( std::max(aRet.Height(), aComboSugg.Height()) );
869
0
    }
870
871
0
    return aRet;
872
0
}
873
874
bool NumericBox::PreNotify( NotifyEvent& rNEvt )
875
0
{
876
0
    if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
877
0
    {
878
0
        if ( ImplNumericProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
879
0
            return true;
880
0
    }
881
882
0
    return ComboBox::PreNotify( rNEvt );
883
0
}
884
885
bool NumericBox::EventNotify( NotifyEvent& rNEvt )
886
0
{
887
0
    if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
888
0
        MarkToBeReformatted( false );
889
0
    else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
890
0
    {
891
0
        if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
892
0
            Reformat();
893
0
    }
894
895
0
    return ComboBox::EventNotify( rNEvt );
896
0
}
897
898
void NumericBox::DataChanged( const DataChangedEvent& rDCEvt )
899
0
{
900
0
    ComboBox::DataChanged( rDCEvt );
901
902
0
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
903
0
    {
904
0
        OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
905
0
        OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
906
0
        ImplResetLocaleDataWrapper();
907
0
        OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
908
0
        OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
909
0
        ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
910
0
        ReformatAll();
911
0
    }
912
0
}
913
914
void NumericBox::Modify()
915
0
{
916
0
    MarkToBeReformatted( true );
917
0
    ComboBox::Modify();
918
0
}
919
920
void NumericBox::ImplNumericReformat( std::u16string_view rStr, sal_Int64& rValue,
921
                                                OUString& rOutStr )
922
0
{
923
0
    if (ImplNumericGetValue(rStr, rValue, GetDecimalDigits(), ImplGetLocaleDataWrapper()))
924
0
    {
925
0
        sal_Int64 nTempVal = ClipAgainstMinMax(rValue);
926
0
        rOutStr = CreateFieldText( nTempVal );
927
0
    }
928
0
}
929
930
void NumericBox::ReformatAll()
931
0
{
932
0
    sal_Int64 nValue;
933
0
    OUString aStr;
934
0
    SetUpdateMode( false );
935
0
    sal_Int32 nEntryCount = GetEntryCount();
936
0
    for ( sal_Int32 i=0; i < nEntryCount; i++ )
937
0
    {
938
0
        ImplNumericReformat( GetEntry( i ), nValue, aStr );
939
0
        RemoveEntryAt(i);
940
0
        InsertEntry( aStr, i );
941
0
    }
942
0
    NumericFormatter::Reformat();
943
0
    SetUpdateMode( true );
944
0
}
945
946
static OUString ImplMetricGetUnitText(std::u16string_view rStr)
947
0
{
948
    // fetch unit text
949
0
    OUStringBuffer aStr;
950
0
    for (sal_Int32 i = static_cast<sal_Int32>(rStr.size())-1; i >= 0; --i)
951
0
    {
952
0
        sal_Unicode c = rStr[i];
953
0
        if ( (c == '\'') || (c == '\"') || (c == '%') || (c == 0x2032) || (c == 0x2033) || unicode::isAlpha(c) || unicode::isControl(c) )
954
0
            aStr.insert(0, c);
955
0
        else
956
0
        {
957
0
            if (!aStr.isEmpty())
958
0
                break;
959
0
        }
960
0
    }
961
0
    return aStr.makeStringAndClear();
962
0
}
963
964
// #104355# support localized measurements
965
966
static OUString ImplMetricToString( FieldUnit rUnit )
967
0
{
968
    // return unit's default string (ie, the first one )
969
0
    for (auto const& elem : ImplGetFieldUnits())
970
0
    {
971
0
        if (elem.second == rUnit)
972
0
            return elem.first;
973
0
    }
974
975
0
    return OUString();
976
0
}
977
978
namespace
979
{
980
    FieldUnit StringToMetric(const OUString &rMetricString)
981
0
    {
982
        // return FieldUnit
983
0
        OUString aStr = rMetricString.toAsciiLowerCase().replaceAll(" ", "");
984
0
        for (auto const& elem : ImplGetCleanedFieldUnits())
985
0
        {
986
0
            if ( elem.first == aStr )
987
0
                return elem.second;
988
0
        }
989
990
0
        return FieldUnit::NONE;
991
0
    }
992
}
993
994
static FieldUnit ImplMetricGetUnit(std::u16string_view rStr)
995
0
{
996
0
    OUString aStr = ImplMetricGetUnitText(rStr);
997
0
    return StringToMetric(aStr);
998
0
}
999
1000
static FieldUnit ImplMap2FieldUnit( MapUnit meUnit, tools::Long& nDecDigits )
1001
0
{
1002
0
    switch( meUnit )
1003
0
    {
1004
0
        case MapUnit::Map100thMM :
1005
0
            nDecDigits -= 2;
1006
0
            return FieldUnit::MM;
1007
0
        case MapUnit::Map10thMM :
1008
0
            nDecDigits -= 1;
1009
0
            return FieldUnit::MM;
1010
0
        case MapUnit::MapMM :
1011
0
            return FieldUnit::MM;
1012
0
        case MapUnit::MapCM :
1013
0
            return FieldUnit::CM;
1014
0
        case MapUnit::Map1000thInch :
1015
0
            nDecDigits -= 3;
1016
0
            return FieldUnit::INCH;
1017
0
        case MapUnit::Map100thInch :
1018
0
            nDecDigits -= 2;
1019
0
            return FieldUnit::INCH;
1020
0
        case MapUnit::Map10thInch :
1021
0
            nDecDigits -= 1;
1022
0
            return FieldUnit::INCH;
1023
0
        case MapUnit::MapInch :
1024
0
            return FieldUnit::INCH;
1025
0
        case MapUnit::MapPoint :
1026
0
            return FieldUnit::POINT;
1027
0
        case MapUnit::MapTwip :
1028
0
            return FieldUnit::TWIP;
1029
0
        default:
1030
0
            OSL_FAIL( "default eInUnit" );
1031
0
            break;
1032
0
    }
1033
0
    return FieldUnit::NONE;
1034
0
}
1035
1036
static double nonValueDoubleToValueDouble( double nValue )
1037
0
{
1038
0
    return std::isfinite( nValue ) ? nValue : 0.0;
1039
0
}
1040
1041
static double ConvertDoubleValue(double nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits,
1042
                                 FieldUnit eInUnit, FieldUnit eOutUnit)
1043
0
{
1044
0
    if ( eInUnit != eOutUnit )
1045
0
    {
1046
0
        if (eInUnit == FieldUnit::PERCENT && mnBaseValue > 0 && nValue > 0)
1047
0
        {
1048
0
            sal_Int64 nDiv = 100 * ImplPower10(nDecDigits);
1049
1050
0
            if (mnBaseValue != 1)
1051
0
                nValue *= mnBaseValue;
1052
1053
0
            nValue += nDiv / 2;
1054
0
            nValue /= nDiv;
1055
0
        }
1056
0
        else
1057
0
        {
1058
0
            const o3tl::Length eFrom = FieldToO3tlLength(eInUnit, o3tl::Length::invalid);
1059
0
            const o3tl::Length eTo = FieldToO3tlLength(eOutUnit, o3tl::Length::invalid);
1060
0
            if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
1061
0
                nValue = o3tl::convert(nValue, eFrom, eTo);
1062
0
        }
1063
0
    }
1064
1065
0
    return nValue;
1066
0
}
1067
1068
namespace vcl
1069
{
1070
    sal_Int64 ConvertValue(sal_Int64 nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits,
1071
                           FieldUnit eInUnit, FieldUnit eOutUnit)
1072
0
    {
1073
0
        double nDouble = nonValueDoubleToValueDouble(ConvertDoubleValue(
1074
0
                    static_cast<double>(nValue), mnBaseValue, nDecDigits, eInUnit, eOutUnit));
1075
1076
0
        return clipDoubleAgainstMinMax(nDouble, SAL_MIN_INT64, SAL_MAX_INT64);
1077
0
    }
1078
}
1079
1080
namespace {
1081
1082
bool checkConversionUnits(FieldUnit eInUnit, FieldUnit eOutUnit)
1083
0
{
1084
0
    return FieldToO3tlLength(eInUnit, o3tl::Length::invalid) != o3tl::Length::invalid
1085
0
        && FieldToO3tlLength(eOutUnit, o3tl::Length::invalid) != o3tl::Length::invalid;
1086
0
}
1087
1088
double convertValue( double nValue, tools::Long nDigits, FieldUnit eInUnit, FieldUnit eOutUnit )
1089
0
{
1090
0
    if ( nDigits < 0 )
1091
0
    {
1092
0
        nValue /= ImplPower10(-nDigits);
1093
0
    }
1094
0
    else
1095
0
    {
1096
0
        nValue *= ImplPower10(nDigits);
1097
0
    }
1098
1099
0
    if ( eInUnit != eOutUnit )
1100
0
    {
1101
0
        const o3tl::Length eFrom = FieldToO3tlLength(eInUnit), eTo = FieldToO3tlLength(eOutUnit);
1102
0
        if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
1103
0
            nValue = o3tl::convert(nValue, eFrom, eTo);
1104
0
    }
1105
1106
0
    return nValue;
1107
0
}
1108
1109
}
1110
1111
namespace vcl
1112
{
1113
    // nValue is not scaled to nDecDigits; the result is scaled
1114
    sal_Int64 ConvertAndScaleValue( sal_Int64 nValue, sal_uInt16 nDigits,
1115
                                         MapUnit eInUnit, FieldUnit eOutUnit )
1116
0
    {
1117
0
        tools::Long nDecDigits = nDigits;
1118
0
        FieldUnit eFieldUnit = ImplMap2FieldUnit( eInUnit, nDecDigits );
1119
1120
0
        if ( !checkConversionUnits(eFieldUnit, eOutUnit) )
1121
0
        {
1122
0
            OSL_FAIL( "invalid parameters" );
1123
0
            return nValue;
1124
0
        }
1125
1126
        // Avoid sal_Int64 <-> double conversion issues if possible:
1127
0
        if (eFieldUnit == eOutUnit && nDecDigits == 0)
1128
0
        {
1129
0
            return nValue;
1130
0
        }
1131
1132
0
        return static_cast<sal_Int64>(
1133
0
            nonValueDoubleToValueDouble(
1134
0
                convertValue( nValue, nDecDigits, eFieldUnit, eOutUnit ) ) );
1135
0
    }
1136
1137
    // nValue is already scaled to nDecDigits; the result is unscaled
1138
    sal_Int64 ConvertAndUnscaleValue(sal_Int64 nValue, sal_uInt16 nDigits, FieldUnit eInUnit,
1139
                                     FieldUnit eOutUnit)
1140
0
    {
1141
0
        if (!checkConversionUnits(eInUnit, eOutUnit))
1142
0
        {
1143
0
            SAL_WARN("vcl", "invalid parameters");
1144
0
            return nValue;
1145
0
        }
1146
1147
        // Avoid sal_Int64 <-> double conversion issues if possible:
1148
0
        if (eInUnit == eOutUnit && nDigits == 0)
1149
0
        {
1150
0
            return nValue;
1151
0
        }
1152
1153
0
        return static_cast<sal_Int64>(
1154
0
            nonValueDoubleToValueDouble(
1155
0
                convertValue( nValue, -static_cast<tools::Long>(nDigits), eInUnit, eOutUnit ) ) );
1156
0
    }
1157
}
1158
1159
namespace vcl
1160
{
1161
    bool TextToValue(std::u16string_view rStr, double& rValue, sal_Int64 nBaseValue,
1162
                     sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper, FieldUnit eUnit)
1163
0
    {
1164
        // Get value
1165
0
        double nValue;
1166
0
        if (!ImplNumericGetDoubleValue(rStr, nValue, nDecDigits, rLocaleDataWrapper))
1167
0
            return false;
1168
1169
        // Determine unit
1170
0
        FieldUnit eEntryUnit = ImplMetricGetUnit( rStr );
1171
1172
        // Recalculate unit
1173
0
        rValue = ConvertDoubleValue(nValue, nBaseValue, nDecDigits, eEntryUnit, eUnit);
1174
1175
0
        return true;
1176
0
    }
1177
1178
0
    FieldUnit GetTextMetricUnit(std::u16string_view aStr) { return ImplMetricGetUnit(aStr); }
1179
}
1180
1181
void MetricFormatter::ImplMetricReformat( std::u16string_view rStr, double& rValue, OUString& rOutStr )
1182
0
{
1183
0
    if (!vcl::TextToValue(rStr, rValue, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit))
1184
0
        return;
1185
1186
0
    rOutStr = CreateFieldText(ClipDoubleAgainstMinMax(rValue));
1187
0
}
1188
1189
MetricFormatter::MetricFormatter(Edit* pEdit)
1190
0
    : NumericFormatter(pEdit)
1191
0
    , meUnit(FieldUnit::NONE)
1192
0
{
1193
0
}
1194
1195
MetricFormatter::~MetricFormatter()
1196
0
{
1197
0
}
1198
1199
void MetricFormatter::SetUnit( FieldUnit eNewUnit )
1200
0
{
1201
0
    if (eNewUnit == FieldUnit::MM_100TH)
1202
0
    {
1203
0
        SetDecimalDigits( GetDecimalDigits() + 2 );
1204
0
        meUnit = FieldUnit::MM;
1205
0
    }
1206
0
    else
1207
0
        meUnit = eNewUnit;
1208
0
    ReformatAll();
1209
0
}
1210
1211
void MetricFormatter::SetCustomUnitText( const OUString& rStr )
1212
0
{
1213
0
    maCustomUnitText = rStr;
1214
0
    ReformatAll();
1215
0
}
1216
1217
void MetricFormatter::SetValue( sal_Int64 nNewValue, FieldUnit eInUnit )
1218
0
{
1219
0
    SetUserValue( nNewValue, eInUnit );
1220
0
}
1221
1222
OUString MetricFormatter::CreateFieldText( sal_Int64 nValue ) const
1223
0
{
1224
    //whether percent is separated from its number is locale
1225
    //specific, pawn it off to icu to decide
1226
0
    if (meUnit == FieldUnit::PERCENT)
1227
0
    {
1228
0
        double dValue = nValue;
1229
0
        dValue /= ImplPower10(GetDecimalDigits());
1230
0
        return unicode::formatPercent(dValue, GetLanguageTag());
1231
0
    }
1232
1233
0
    OUString aStr = NumericFormatter::CreateFieldText( nValue );
1234
1235
0
    if( meUnit == FieldUnit::CUSTOM )
1236
0
        aStr += maCustomUnitText;
1237
0
    else
1238
0
    {
1239
0
        OUString aSuffix = ImplMetricToString( meUnit );
1240
0
        if (meUnit != FieldUnit::NONE && meUnit != FieldUnit::DEGREE && meUnit != FieldUnit::INCH && meUnit != FieldUnit::FOOT)
1241
0
            aStr += " ";
1242
0
        if (meUnit == FieldUnit::INCH)
1243
0
        {
1244
0
            OUString sDoublePrime = u"\u2033"_ustr;
1245
0
            if (aSuffix != "\"" && aSuffix != sDoublePrime)
1246
0
                aStr += " ";
1247
0
            else
1248
0
                aSuffix = sDoublePrime;
1249
0
        }
1250
0
        else if (meUnit == FieldUnit::FOOT)
1251
0
        {
1252
0
            OUString sPrime = u"\u2032"_ustr;
1253
0
            if (aSuffix != "'" && aSuffix != sPrime)
1254
0
                aStr += " ";
1255
0
            else
1256
0
                aSuffix = sPrime;
1257
0
        }
1258
1259
0
        assert(meUnit != FieldUnit::PERCENT);
1260
0
        aStr += aSuffix;
1261
0
    }
1262
0
    return aStr;
1263
0
}
1264
1265
void MetricFormatter::SetUserValue( sal_Int64 nNewValue, FieldUnit eInUnit )
1266
0
{
1267
    // convert to previously configured units
1268
0
    nNewValue = vcl::ConvertValue( nNewValue, 0, GetDecimalDigits(), eInUnit, meUnit );
1269
0
    NumericFormatter::SetUserValue( nNewValue );
1270
0
}
1271
1272
sal_Int64 MetricFormatter::GetValueFromStringUnit(std::u16string_view rStr, FieldUnit eOutUnit) const
1273
0
{
1274
0
    double nTempValue;
1275
    // caution: precision loss in double cast
1276
0
    if (!vcl::TextToValue(rStr, nTempValue, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit))
1277
0
        nTempValue = static_cast<double>(mnLastValue);
1278
1279
    // convert to requested units
1280
0
    return vcl::ConvertValue(ClipDoubleAgainstMinMax(nTempValue), 0, GetDecimalDigits(), meUnit, eOutUnit);
1281
0
}
1282
1283
sal_Int64 MetricFormatter::GetValueFromString(std::u16string_view rStr) const
1284
0
{
1285
0
    return GetValueFromStringUnit(rStr, FieldUnit::NONE);
1286
0
}
1287
1288
sal_Int64 MetricFormatter::GetValue( FieldUnit eOutUnit ) const
1289
0
{
1290
0
    return GetField() ? GetValueFromStringUnit(GetField()->GetText(), eOutUnit) : 0;
1291
0
}
1292
1293
void MetricFormatter::SetValue( sal_Int64 nValue )
1294
0
{
1295
    // Implementation not inline, because it is a virtual Function
1296
0
    SetValue( nValue, FieldUnit::NONE );
1297
0
}
1298
1299
void MetricFormatter::SetMin( sal_Int64 nNewMin, FieldUnit eInUnit )
1300
0
{
1301
    // convert to requested units
1302
0
    NumericFormatter::SetMin(vcl::ConvertValue(nNewMin, 0, GetDecimalDigits(), eInUnit, meUnit));
1303
0
}
1304
1305
sal_Int64 MetricFormatter::GetMin( FieldUnit eOutUnit ) const
1306
0
{
1307
    // convert to requested units
1308
0
    return vcl::ConvertValue(NumericFormatter::GetMin(), 0, GetDecimalDigits(), meUnit, eOutUnit);
1309
0
}
1310
1311
void MetricFormatter::SetMax( sal_Int64 nNewMax, FieldUnit eInUnit )
1312
0
{
1313
    // convert to requested units
1314
0
    NumericFormatter::SetMax(vcl::ConvertValue(nNewMax, 0, GetDecimalDigits(), eInUnit, meUnit));
1315
0
}
1316
1317
sal_Int64 MetricFormatter::GetMax( FieldUnit eOutUnit ) const
1318
0
{
1319
    // convert to requested units
1320
0
    return vcl::ConvertValue(NumericFormatter::GetMax(), 0, GetDecimalDigits(), meUnit, eOutUnit);
1321
0
}
1322
1323
void MetricFormatter::Reformat()
1324
0
{
1325
0
    if ( !GetField() )
1326
0
        return;
1327
1328
0
    OUString aText = GetField()->GetText();
1329
1330
0
    OUString aStr;
1331
    // caution: precision loss in double cast
1332
0
    double nTemp = static_cast<double>(mnLastValue);
1333
0
    ImplMetricReformat( aText, nTemp, aStr );
1334
0
    mnLastValue = static_cast<sal_Int64>(nTemp);
1335
1336
0
    if ( !aStr.isEmpty() )
1337
0
    {
1338
0
        ImplSetText( aStr );
1339
0
    }
1340
0
    else
1341
0
        SetValue( mnLastValue );
1342
0
}
1343
1344
sal_Int64 MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit ) const
1345
0
{
1346
    // convert to requested units
1347
0
    return vcl::ConvertValue(0/*nCorrectedValue*/, 0, GetDecimalDigits(),
1348
0
                             meUnit, eOutUnit);
1349
0
}
1350
1351
MetricField::MetricField(vcl::Window* pParent, WinBits nWinStyle)
1352
0
    : SpinField(pParent, nWinStyle, WindowType::METRICFIELD)
1353
0
    , MetricFormatter(this)
1354
0
{
1355
0
    Reformat();
1356
0
}
Unexecuted instantiation: MetricField::MetricField(vcl::Window*, long)
Unexecuted instantiation: MetricField::MetricField(vcl::Window*, long)
1357
1358
void MetricField::dispose()
1359
0
{
1360
0
    ClearField();
1361
0
    SpinField::dispose();
1362
0
}
1363
1364
Size MetricField::CalcMinimumSize() const
1365
0
{
1366
0
    return calcMinimumSize(*this, *this);
1367
0
}
1368
1369
bool MetricField::set_property(const OUString &rKey, const OUString &rValue)
1370
0
{
1371
0
    if (rKey == "digits")
1372
0
        SetDecimalDigits(rValue.toInt32());
1373
0
    else if (rKey == "spin-size")
1374
0
        SetSpinSize(rValue.toInt32());
1375
0
    else
1376
0
        return SpinField::set_property(rKey, rValue);
1377
0
    return true;
1378
0
}
1379
1380
void MetricField::SetUnit( FieldUnit nNewUnit )
1381
0
{
1382
0
    sal_Int64 nRawMax = GetMax( nNewUnit );
1383
0
    sal_Int64 nMax = Denormalize( nRawMax );
1384
0
    sal_Int64 nMin = Denormalize( GetMin( nNewUnit ) );
1385
0
    sal_Int64 nFirst = Denormalize( GetFirst( nNewUnit ) );
1386
0
    sal_Int64 nLast = Denormalize( GetLast( nNewUnit ) );
1387
1388
0
    MetricFormatter::SetUnit( nNewUnit );
1389
1390
0
    SetMax( Normalize( nMax ), nNewUnit );
1391
0
    SetMin( Normalize( nMin ), nNewUnit );
1392
0
    SetFirst( Normalize( nFirst ), nNewUnit );
1393
0
    SetLast( Normalize( nLast ), nNewUnit );
1394
0
}
1395
1396
void MetricField::SetFirst( sal_Int64 nNewFirst, FieldUnit eInUnit )
1397
0
{
1398
    // convert
1399
0
    nNewFirst = vcl::ConvertValue(nNewFirst, 0, GetDecimalDigits(), eInUnit, meUnit);
1400
0
    mnFirst = nNewFirst;
1401
0
}
1402
1403
sal_Int64 MetricField::GetFirst( FieldUnit eOutUnit ) const
1404
0
{
1405
    // convert
1406
0
    return vcl::ConvertValue(mnFirst, 0, GetDecimalDigits(), meUnit, eOutUnit);
1407
0
}
1408
1409
void MetricField::SetLast( sal_Int64 nNewLast, FieldUnit eInUnit )
1410
0
{
1411
    // convert
1412
0
    nNewLast = vcl::ConvertValue(nNewLast, 0, GetDecimalDigits(), eInUnit, meUnit);
1413
0
    mnLast = nNewLast;
1414
0
}
1415
1416
sal_Int64 MetricField::GetLast( FieldUnit eOutUnit ) const
1417
0
{
1418
    // convert
1419
0
    return vcl::ConvertValue(mnLast, 0, GetDecimalDigits(), meUnit, eOutUnit);
1420
0
}
1421
1422
bool MetricField::EventNotify( NotifyEvent& rNEvt )
1423
0
{
1424
0
    if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
1425
0
        MarkToBeReformatted( false );
1426
0
    else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
1427
0
    {
1428
0
        if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1429
0
            Reformat();
1430
0
    }
1431
1432
0
    return SpinField::EventNotify( rNEvt );
1433
0
}
1434
1435
void MetricField::DataChanged( const DataChangedEvent& rDCEvt )
1436
0
{
1437
0
    SpinField::DataChanged( rDCEvt );
1438
1439
0
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
1440
0
    {
1441
0
        OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1442
0
        OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1443
0
        ImplResetLocaleDataWrapper();
1444
0
        OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1445
0
        OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1446
0
        ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1447
0
        ReformatAll();
1448
0
    }
1449
0
}
1450
1451
void MetricField::Modify()
1452
0
{
1453
0
    MarkToBeReformatted( true );
1454
0
    SpinField::Modify();
1455
0
}
1456
1457
void MetricField::Up()
1458
0
{
1459
0
    FieldUp();
1460
0
    SpinField::Up();
1461
0
}
1462
1463
void MetricField::Down()
1464
0
{
1465
0
    FieldDown();
1466
0
    SpinField::Down();
1467
0
}
1468
1469
void MetricField::First()
1470
0
{
1471
0
    FieldFirst();
1472
0
    SpinField::First();
1473
0
}
1474
1475
void MetricField::Last()
1476
0
{
1477
0
    FieldLast();
1478
0
    SpinField::Last();
1479
0
}
1480
1481
void MetricField::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1482
0
{
1483
0
    SpinField::DumpAsPropertyTree(rJsonWriter);
1484
0
    rJsonWriter.put("min", GetMin());
1485
0
    rJsonWriter.put("max", GetMax());
1486
0
    rJsonWriter.put("unit", FieldUnitToString(GetUnit()));
1487
0
    OUString sValue = Application::GetSettings().GetNeutralLocaleDataWrapper().
1488
0
        getNum(GetValue(), GetDecimalDigits(), false, false);
1489
0
    rJsonWriter.put("value", sValue);
1490
0
}
1491
1492
FactoryFunction MetricField::GetUITestFactory() const
1493
0
{
1494
0
    return MetricFieldUIObject::create;
1495
0
}
1496
1497
MetricBox::MetricBox(vcl::Window* pParent, WinBits nWinStyle)
1498
0
    : ComboBox(pParent, nWinStyle)
1499
0
    , MetricFormatter(this)
1500
0
{
1501
0
    Reformat();
1502
0
}
Unexecuted instantiation: MetricBox::MetricBox(vcl::Window*, long)
Unexecuted instantiation: MetricBox::MetricBox(vcl::Window*, long)
1503
1504
void MetricBox::dispose()
1505
0
{
1506
0
    ClearField();
1507
0
    ComboBox::dispose();
1508
0
}
1509
1510
Size MetricBox::CalcMinimumSize() const
1511
0
{
1512
0
    Size aRet(calcMinimumSize(*this, *this));
1513
1514
0
    if (IsDropDownBox())
1515
0
    {
1516
0
        Size aComboSugg(ComboBox::CalcMinimumSize());
1517
0
        aRet.setWidth( std::max(aRet.Width(), aComboSugg.Width()) );
1518
0
        aRet.setHeight( std::max(aRet.Height(), aComboSugg.Height()) );
1519
0
    }
1520
1521
0
    return aRet;
1522
0
}
1523
1524
bool MetricBox::EventNotify( NotifyEvent& rNEvt )
1525
0
{
1526
0
    if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
1527
0
        MarkToBeReformatted( false );
1528
0
    else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
1529
0
    {
1530
0
        if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1531
0
            Reformat();
1532
0
    }
1533
1534
0
    return ComboBox::EventNotify( rNEvt );
1535
0
}
1536
1537
void MetricBox::DataChanged( const DataChangedEvent& rDCEvt )
1538
0
{
1539
0
    ComboBox::DataChanged( rDCEvt );
1540
1541
0
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
1542
0
    {
1543
0
        OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1544
0
        OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1545
0
        ImplResetLocaleDataWrapper();
1546
0
        OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1547
0
        OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1548
0
        ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1549
0
        ReformatAll();
1550
0
    }
1551
0
}
1552
1553
void MetricBox::Modify()
1554
0
{
1555
0
    MarkToBeReformatted( true );
1556
0
    ComboBox::Modify();
1557
0
}
1558
1559
void MetricBox::ReformatAll()
1560
0
{
1561
0
    double nValue;
1562
0
    OUString aStr;
1563
0
    SetUpdateMode( false );
1564
0
    sal_Int32 nEntryCount = GetEntryCount();
1565
0
    for ( sal_Int32 i=0; i < nEntryCount; i++ )
1566
0
    {
1567
0
        ImplMetricReformat( GetEntry( i ), nValue, aStr );
1568
0
        RemoveEntryAt(i);
1569
0
        InsertEntry( aStr, i );
1570
0
    }
1571
0
    MetricFormatter::Reformat();
1572
0
    SetUpdateMode( true );
1573
0
}
1574
1575
static bool ImplCurrencyGetValue( std::u16string_view rStr, sal_Int64& rValue,
1576
                                  sal_uInt16 nDecDigits, const LocaleDataWrapper& rWrapper )
1577
0
{
1578
    // fetch number
1579
0
    return ImplNumericGetValue( rStr, rValue, nDecDigits, rWrapper, true );
1580
0
}
1581
1582
void CurrencyFormatter::ImplCurrencyReformat(std::u16string_view rStr, OUString& rOutStr)
1583
0
{
1584
0
    sal_Int64 nValue;
1585
0
    if ( !ImplNumericGetValue( rStr, nValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), true ) )
1586
0
        return;
1587
0
    rOutStr = CreateFieldText(ClipAgainstMinMax(nValue));
1588
0
}
1589
1590
CurrencyFormatter::CurrencyFormatter(Edit* pField)
1591
0
    : NumericFormatter(pField)
1592
0
{
1593
0
}
1594
1595
CurrencyFormatter::~CurrencyFormatter()
1596
{
1597
}
1598
1599
void CurrencyFormatter::SetValue( sal_Int64 nNewValue )
1600
0
{
1601
0
    SetUserValue( nNewValue );
1602
0
    SetEmptyFieldValueData( false );
1603
0
}
1604
1605
OUString CurrencyFormatter::CreateFieldText( sal_Int64 nValue ) const
1606
0
{
1607
0
    return ImplGetLocaleDataWrapper().getCurr( nValue, GetDecimalDigits(),
1608
0
                                               ImplGetLocaleDataWrapper().getCurrSymbol(),
1609
0
                                               IsUseThousandSep() );
1610
0
}
1611
1612
sal_Int64 CurrencyFormatter::GetValueFromString(std::u16string_view rStr) const
1613
0
{
1614
0
    sal_Int64 nTempValue;
1615
0
    if ( ImplCurrencyGetValue( rStr, nTempValue, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
1616
0
    {
1617
0
        return ClipAgainstMinMax(nTempValue);
1618
0
    }
1619
0
    else
1620
0
        return mnLastValue;
1621
0
}
1622
1623
void CurrencyFormatter::Reformat()
1624
0
{
1625
0
    if ( !GetField() )
1626
0
        return;
1627
1628
0
    OUString aStr;
1629
0
    ImplCurrencyReformat( GetField()->GetText(), aStr );
1630
1631
0
    if ( !aStr.isEmpty() )
1632
0
    {
1633
0
        ImplSetText( aStr  );
1634
0
        sal_Int64 nTemp = mnLastValue;
1635
0
        ImplCurrencyGetValue( aStr, nTemp, GetDecimalDigits(), ImplGetLocaleDataWrapper() );
1636
0
        mnLastValue = nTemp;
1637
0
    }
1638
0
    else
1639
0
        SetValue( mnLastValue );
1640
0
}
1641
1642
CurrencyField::CurrencyField(vcl::Window* pParent, WinBits nWinStyle)
1643
0
    : SpinField(pParent, nWinStyle)
1644
0
    , CurrencyFormatter(this)
1645
0
{
1646
0
    Reformat();
1647
0
}
Unexecuted instantiation: CurrencyField::CurrencyField(vcl::Window*, long)
Unexecuted instantiation: CurrencyField::CurrencyField(vcl::Window*, long)
1648
1649
void CurrencyField::dispose()
1650
0
{
1651
0
    ClearField();
1652
0
    SpinField::dispose();
1653
0
}
1654
1655
bool CurrencyField::EventNotify( NotifyEvent& rNEvt )
1656
0
{
1657
0
    if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
1658
0
        MarkToBeReformatted( false );
1659
0
    else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
1660
0
    {
1661
0
        if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1662
0
            Reformat();
1663
0
    }
1664
1665
0
    return SpinField::EventNotify( rNEvt );
1666
0
}
1667
1668
void CurrencyField::DataChanged( const DataChangedEvent& rDCEvt )
1669
0
{
1670
0
    SpinField::DataChanged( rDCEvt );
1671
1672
0
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
1673
0
    {
1674
0
        OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1675
0
        OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1676
0
        ImplResetLocaleDataWrapper();
1677
0
        OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1678
0
        OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1679
0
        ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1680
0
        ReformatAll();
1681
0
    }
1682
0
}
1683
1684
void CurrencyField::Modify()
1685
0
{
1686
0
    MarkToBeReformatted( true );
1687
0
    SpinField::Modify();
1688
0
}
1689
1690
void CurrencyField::Up()
1691
0
{
1692
0
    FieldUp();
1693
0
    SpinField::Up();
1694
0
}
1695
1696
void CurrencyField::Down()
1697
0
{
1698
0
    FieldDown();
1699
0
    SpinField::Down();
1700
0
}
1701
1702
void CurrencyField::First()
1703
0
{
1704
0
    FieldFirst();
1705
0
    SpinField::First();
1706
0
}
1707
1708
void CurrencyField::Last()
1709
0
{
1710
0
    FieldLast();
1711
0
    SpinField::Last();
1712
0
}
1713
1714
CurrencyBox::CurrencyBox(vcl::Window* pParent, WinBits nWinStyle)
1715
0
    : ComboBox(pParent, nWinStyle)
1716
0
    , CurrencyFormatter(this)
1717
0
{
1718
0
    Reformat();
1719
0
}
Unexecuted instantiation: CurrencyBox::CurrencyBox(vcl::Window*, long)
Unexecuted instantiation: CurrencyBox::CurrencyBox(vcl::Window*, long)
1720
1721
void CurrencyBox::dispose()
1722
0
{
1723
0
    ClearField();
1724
0
    ComboBox::dispose();
1725
0
}
1726
1727
bool CurrencyBox::EventNotify( NotifyEvent& rNEvt )
1728
0
{
1729
0
    if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
1730
0
        MarkToBeReformatted( false );
1731
0
    else if ( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
1732
0
    {
1733
0
        if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1734
0
            Reformat();
1735
0
    }
1736
1737
0
    return ComboBox::EventNotify( rNEvt );
1738
0
}
1739
1740
void CurrencyBox::DataChanged( const DataChangedEvent& rDCEvt )
1741
0
{
1742
0
    ComboBox::DataChanged( rDCEvt );
1743
1744
0
    if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
1745
0
    {
1746
0
        OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1747
0
        OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1748
0
        ImplResetLocaleDataWrapper();
1749
0
        OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1750
0
        OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1751
0
        ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1752
0
        ReformatAll();
1753
0
    }
1754
0
}
1755
1756
void CurrencyBox::Modify()
1757
0
{
1758
0
    MarkToBeReformatted( true );
1759
0
    ComboBox::Modify();
1760
0
}
1761
1762
void CurrencyBox::ReformatAll()
1763
0
{
1764
0
    OUString aStr;
1765
0
    SetUpdateMode( false );
1766
0
    sal_Int32 nEntryCount = GetEntryCount();
1767
0
    for ( sal_Int32 i=0; i < nEntryCount; i++ )
1768
0
    {
1769
0
        ImplCurrencyReformat( GetEntry( i ), aStr );
1770
0
        RemoveEntryAt(i);
1771
0
        InsertEntry( aStr, i );
1772
0
    }
1773
0
    CurrencyFormatter::Reformat();
1774
0
    SetUpdateMode( true );
1775
0
}
1776
1777
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */