Bug Summary

File:home/maarten/src/libreoffice/core/vcl/source/control/field.cxx
Warning:line 677, column 34
Division by zero

Annotated Source Code

Press '?' to see keyboard shortcuts

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