1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
23 #include <string_view>
25 #include <sal/log.hxx>
26 #include <o3tl/string_view.hxx>
27 #include <osl/diagnose.h>
29 #include <comphelper/string.hxx>
30 #include <tools/UnitConversion.hxx>
32 #include <vcl/builder.hxx>
33 #include <vcl/fieldvalues.hxx>
34 #include <vcl/toolkit/field.hxx>
35 #include <vcl/event.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/settings.hxx>
38 #include <vcl/uitest/uiobject.hxx>
39 #include <vcl/uitest/metricfielduiobject.hxx>
43 #include <i18nutil/unicode.hxx>
45 #include <rtl/math.hxx>
47 #include <unotools/localedatawrapper.hxx>
48 #include <boost/property_tree/ptree.hpp>
49 #include <tools/json_writer.hxx>
51 using namespace ::com::sun::star
;
52 using namespace ::comphelper
;
57 std::string
FieldUnitToString(FieldUnit unit
)
79 case FieldUnit::POINT
:
100 case FieldUnit::CUSTOM
:
103 case FieldUnit::PERCENT
:
106 case FieldUnit::MM_100TH
:
109 case FieldUnit::PIXEL
:
112 case FieldUnit::DEGREE
:
115 case FieldUnit::SECOND
:
118 case FieldUnit::MILLISECOND
:
119 return "millisecond";
125 sal_Int64
ImplPower10( sal_uInt16 n
)
128 sal_Int64 nValue
= 1;
130 for ( i
=0; i
< n
; i
++ )
136 bool ImplNumericProcessKeyInput( const KeyEvent
& rKEvt
,
137 bool bStrictFormat
, bool bThousandSep
,
138 const LocaleDataWrapper
& rLocaleDataWrapper
)
140 if ( !bStrictFormat
)
144 sal_Unicode cChar
= rKEvt
.GetCharCode();
145 sal_uInt16 nGroup
= rKEvt
.GetKeyCode().GetGroup();
147 return !((nGroup
== KEYGROUP_FKEYS
) ||
148 (nGroup
== KEYGROUP_CURSOR
) ||
149 (nGroup
== KEYGROUP_MISC
) ||
150 ((cChar
>= '0') && (cChar
<= '9')) ||
151 rLocaleDataWrapper
.getNumDecimalSep() == OUStringChar(cChar
) ||
152 (bThousandSep
&& rLocaleDataWrapper
.getNumThousandSep() == OUStringChar(cChar
)) ||
153 rLocaleDataWrapper
.getNumDecimalSepAlt() == OUStringChar(cChar
) ||
158 bool ImplNumericGetValue( const OUString
& rStr
, sal_Int64
& rValue
,
159 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
,
160 bool bCurrency
= false )
162 OUString aStr
= rStr
;
163 OUStringBuffer aStr1
, aStr2
, aStrNum
, aStrDenom
;
164 bool bNegative
= false;
166 sal_Int32 nDecPos
, nFracDivPos
;
169 // react on empty string
170 if ( rStr
.isEmpty() )
173 // remove leading and trailing spaces
177 // find position of decimal point
178 nDecPos
= aStr
.indexOf( rLocaleDataWrapper
.getNumDecimalSep() );
179 if (nDecPos
< 0 && !rLocaleDataWrapper
.getNumDecimalSepAlt().isEmpty())
180 nDecPos
= aStr
.indexOf( rLocaleDataWrapper
.getNumDecimalSepAlt() );
181 // find position of fraction
182 nFracDivPos
= aStr
.indexOf( '/' );
184 // parse fractional strings
188 sal_Int32 nFracNumPos
= aStr
.lastIndexOf(' ', nFracDivPos
);
190 // If in "a b/c" format.
191 if(nFracNumPos
!= -1 )
193 aStr1
.append(aStr
.subView(0, nFracNumPos
));
194 aStrNum
.append(aStr
.subView(nFracNumPos
+1, nFracDivPos
-nFracNumPos
-1));
195 aStrDenom
.append(aStr
.subView(nFracDivPos
+1));
197 // "a/b" format, or not a fraction at all
200 aStrNum
.append(aStr
.subView(0, nFracDivPos
));
201 aStrDenom
.append(aStr
.subView(nFracDivPos
+1));
205 // parse decimal strings
206 else if ( nDecPos
>= 0)
208 aStr1
.append(aStr
.subView(0, nDecPos
));
209 aStr2
.append(aStr
.subView(nDecPos
+1));
217 if ( aStr
.startsWith("(") && aStr
.endsWith(")") )
221 for (sal_Int32 i
=0; i
< aStr
.getLength(); i
++ )
223 if ( (aStr
[i
] >= '0') && (aStr
[i
] <= '9') )
225 else if ( aStr
[i
] == '-' )
232 if (!bNegative
&& !aStr
.isEmpty())
234 sal_uInt16 nFormat
= rLocaleDataWrapper
.getCurrNegativeFormat();
235 if ( (nFormat
== 3) || (nFormat
== 6) || // $1- || 1-$
236 (nFormat
== 7) || (nFormat
== 10) ) // 1$- || 1 $-
238 for (sal_Int32 i
= aStr
.getLength()-1; i
> 0; --i
)
240 if ( (aStr
[i
] >= '0') && (aStr
[i
] <= '9') )
242 else if ( aStr
[i
] == '-' )
253 if ( !aStr1
.isEmpty() && aStr1
[0] == '-')
255 if ( !aStrNum
.isEmpty() && aStrNum
[0] == '-') // For non-mixed fractions
259 // remove all unwanted characters
261 for (sal_Int32 i
=0; i
< aStr1
.getLength(); )
263 if ( (aStr1
[i
] >= '0') && (aStr1
[i
] <= '9') )
266 aStr1
.remove( i
, 1 );
270 for (sal_Int32 i
=0; i
< aStr2
.getLength(); )
272 if ((aStr2
[i
] >= '0') && (aStr2
[i
] <= '9'))
280 for (sal_Int32 i
=0; i
< aStrNum
.getLength(); )
282 if ((aStrNum
[i
] >= '0') && (aStrNum
[i
] <= '9'))
285 aStrNum
.remove(i
, 1);
288 for (sal_Int32 i
=0; i
< aStrDenom
.getLength(); )
290 if ((aStrDenom
[i
] >= '0') && (aStrDenom
[i
] <= '9'))
293 aStrDenom
.remove(i
, 1);
298 if ( !bFrac
&& aStr1
.isEmpty() && aStr2
.isEmpty() )
300 else if ( bFrac
&& aStr1
.isEmpty() && (aStrNum
.isEmpty() || aStrDenom
.isEmpty()) )
303 if ( aStr1
.isEmpty() )
306 aStr1
.insert(0, "-");
308 // Convert fractional strings
310 // Convert to fraction
311 sal_Int64 nWholeNum
= o3tl::toInt64(aStr1
);
313 sal_Int64 nNum
= o3tl::toInt64(aStrNum
);
314 sal_Int64 nDenom
= o3tl::toInt64(aStrDenom
);
315 if (nDenom
== 0) return false; // Division by zero
316 double nFrac2Dec
= nWholeNum
+ static_cast<double>(nNum
)/nDenom
; // Convert to double for floating point precision
317 OUStringBuffer
aStrFrac(OUString::number(nFrac2Dec
));
318 // Reconvert division result to string and parse
319 nDecPos
= aStrFrac
.indexOf('.');
322 aStr1
.append(aStrFrac
.getStr(), nDecPos
);
323 aStr2
.append(aStrFrac
.getStr()+nDecPos
+1);
329 // prune and round fraction
331 if (aStr2
.getLength() > nDecDigits
)
333 if (aStr2
[nDecDigits
] >= '5')
335 string::truncateToLength(aStr2
, nDecDigits
);
337 if (aStr2
.getLength() < nDecDigits
)
338 string::padToLength(aStr2
, nDecDigits
, '0');
340 aStr
= aStr1
+ aStr2
;
343 nValue
= aStr
.toInt64();
346 // check if string is equivalent to zero
347 sal_Int32 nIndex
= bNegative
? 1 : 0;
348 while (nIndex
< aStr
.getLength() && aStr
[nIndex
] == '0')
350 if( nIndex
< aStr
.getLength() )
352 rValue
= bNegative
? SAL_MIN_INT64
: SAL_MAX_INT64
;
369 void ImplUpdateSeparatorString( OUString
& io_rText
,
370 std::u16string_view rOldDecSep
, std::u16string_view rNewDecSep
,
371 std::u16string_view rOldThSep
, std::u16string_view rNewThSep
)
373 OUStringBuffer
aBuf( io_rText
.getLength() );
374 sal_Int32 nIndexDec
= 0, nIndexTh
= 0, nIndex
= 0;
376 const sal_Unicode
* pBuffer
= io_rText
.getStr();
377 while( nIndex
!= -1 )
379 nIndexDec
= io_rText
.indexOf( rOldDecSep
, nIndex
);
380 nIndexTh
= io_rText
.indexOf( rOldThSep
, nIndex
);
381 if( (nIndexTh
!= -1 && nIndexDec
!= -1 && nIndexTh
< nIndexDec
)
382 || (nIndexTh
!= -1 && nIndexDec
== -1)
385 aBuf
.append( OUString::Concat(std::u16string_view(pBuffer
+ nIndex
, nIndexTh
- nIndex
)) + rNewThSep
);
386 nIndex
= nIndexTh
+ rOldThSep
.size();
388 else if( nIndexDec
!= -1 )
390 aBuf
.append( OUString::Concat(std::u16string_view(pBuffer
+ nIndex
, nIndexDec
- nIndex
)) + rNewDecSep
);
391 nIndex
= nIndexDec
+ rOldDecSep
.size();
395 aBuf
.append( pBuffer
+ nIndex
);
400 io_rText
= aBuf
.makeStringAndClear();
403 void ImplUpdateSeparators( std::u16string_view rOldDecSep
, std::u16string_view rNewDecSep
,
404 std::u16string_view rOldThSep
, std::u16string_view rNewThSep
,
407 bool bChangeDec
= (rOldDecSep
!= rNewDecSep
);
408 bool bChangeTh
= (rOldThSep
!= rNewThSep
);
410 if( !(bChangeDec
|| bChangeTh
) )
413 bool bUpdateMode
= pEdit
->IsUpdateMode();
414 pEdit
->SetUpdateMode( false );
415 OUString aText
= pEdit
->GetText();
416 ImplUpdateSeparatorString( aText
, rOldDecSep
, rNewDecSep
, rOldThSep
, rNewThSep
);
417 pEdit
->SetText( aText
);
419 ComboBox
* pCombo
= dynamic_cast<ComboBox
*>(pEdit
);
422 // update box entries
423 sal_Int32 nEntryCount
= pCombo
->GetEntryCount();
424 for ( sal_Int32 i
=0; i
< nEntryCount
; i
++ )
426 aText
= pCombo
->GetEntry( i
);
427 void* pEntryData
= pCombo
->GetEntryData( i
);
428 ImplUpdateSeparatorString( aText
, rOldDecSep
, rNewDecSep
, rOldThSep
, rNewThSep
);
429 pCombo
->RemoveEntryAt(i
);
430 pCombo
->InsertEntry( aText
, i
);
431 pCombo
->SetEntryData( i
, pEntryData
);
435 pEdit
->SetUpdateMode( bUpdateMode
);
440 FormatterBase::FormatterBase(Edit
* pField
)
443 mpLocaleDataWrapper
= nullptr;
445 mbStrictFormat
= false;
446 mbEmptyFieldValue
= false;
447 mbEmptyFieldValueEnabled
= false;
450 FormatterBase::~FormatterBase()
454 LocaleDataWrapper
& FormatterBase::ImplGetLocaleDataWrapper() const
456 if ( !mpLocaleDataWrapper
)
458 mpLocaleDataWrapper
.reset( new LocaleDataWrapper( GetLanguageTag() ) );
460 return *mpLocaleDataWrapper
;
463 /** reset the LocaleDataWrapper when the language tag changes */
464 void FormatterBase::ImplResetLocaleDataWrapper() const
466 // just get rid of, the next time it is requested, it will get loaded with the right
468 mpLocaleDataWrapper
.reset();
471 const LocaleDataWrapper
& FormatterBase::GetLocaleDataWrapper() const
473 return ImplGetLocaleDataWrapper();
476 void FormatterBase::Reformat()
480 void FormatterBase::ReformatAll()
485 void FormatterBase::SetStrictFormat( bool bStrict
)
487 if ( bStrict
!= mbStrictFormat
)
489 mbStrictFormat
= bStrict
;
490 if ( mbStrictFormat
)
495 const lang::Locale
& FormatterBase::GetLocale() const
498 return mpField
->GetSettings().GetLanguageTag().getLocale();
500 return Application::GetSettings().GetLanguageTag().getLocale();
503 const LanguageTag
& FormatterBase::GetLanguageTag() const
506 return mpField
->GetSettings().GetLanguageTag();
508 return Application::GetSettings().GetLanguageTag();
511 void FormatterBase::ImplSetText( const OUString
& rText
, Selection
const * pNewSelection
)
516 mpField
->SetText(rText
, *pNewSelection
);
519 Selection aSel
= mpField
->GetSelection();
520 aSel
.Min() = aSel
.Max();
521 mpField
->SetText(rText
, aSel
);
523 MarkToBeReformatted( false );
527 void FormatterBase::SetEmptyFieldValue()
530 mpField
->SetText( OUString() );
531 mbEmptyFieldValue
= true;
534 bool FormatterBase::IsEmptyFieldValue() const
536 return (!mpField
|| mpField
->GetText().isEmpty());
539 void NumericFormatter::FormatValue(Selection
const * pNewSelection
)
542 ImplSetText(CreateFieldText(mnLastValue
), pNewSelection
);
543 mbFormatting
= false;
546 void NumericFormatter::ImplNumericReformat()
548 mnLastValue
= GetValue();
552 NumericFormatter::NumericFormatter(Edit
* pEdit
)
553 : FormatterBase(pEdit
)
556 // a "large" value substantially smaller than SAL_MAX_INT64, to avoid
557 // overflow in computations using this "dummy" value
558 , mnMax(SAL_MAX_INT32
)
559 , mbFormatting(false)
565 , mbThousandSep(true)
570 NumericFormatter::~NumericFormatter()
574 void NumericFormatter::SetMin( sal_Int64 nNewMin
)
577 if ( !IsEmptyFieldValue() )
581 void NumericFormatter::SetMax( sal_Int64 nNewMax
)
584 if ( !IsEmptyFieldValue() )
588 void NumericFormatter::SetUseThousandSep( bool bValue
)
590 mbThousandSep
= bValue
;
594 void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits
)
596 mnDecimalDigits
= nDigits
;
600 void NumericFormatter::SetValue( sal_Int64 nNewValue
)
602 SetUserValue( nNewValue
);
603 SetEmptyFieldValueData( false );
606 OUString
NumericFormatter::CreateFieldText( sal_Int64 nValue
) const
608 return ImplGetLocaleDataWrapper().getNum( nValue
, GetDecimalDigits(), IsUseThousandSep(), /*ShowTrailingZeros*/true );
611 void NumericFormatter::ImplSetUserValue( sal_Int64 nNewValue
, Selection
const * pNewSelection
)
613 nNewValue
= ClipAgainstMinMax(nNewValue
);
614 mnLastValue
= nNewValue
;
617 FormatValue(pNewSelection
);
620 void NumericFormatter::SetUserValue( sal_Int64 nNewValue
)
622 ImplSetUserValue( nNewValue
);
625 sal_Int64
NumericFormatter::GetValueFromString(const OUString
& rStr
) const
627 sal_Int64 nTempValue
;
629 if (ImplNumericGetValue(rStr
, nTempValue
,
630 GetDecimalDigits(), ImplGetLocaleDataWrapper()))
632 return ClipAgainstMinMax(nTempValue
);
638 OUString
NumericFormatter::GetValueString() const
640 return Application::GetSettings().GetNeutralLocaleDataWrapper().
641 getNum(GetValue(), GetDecimalDigits(), false, false);
644 // currently used by online
645 void NumericFormatter::SetValueFromString(const OUString
& rStr
)
649 if (ImplNumericGetValue(rStr
, nValue
, GetDecimalDigits(),
650 Application::GetSettings().GetNeutralLocaleDataWrapper()))
652 ImplNewFieldValue(nValue
);
656 SAL_WARN("vcl", "fail to convert the value: " << rStr
);
660 sal_Int64
NumericFormatter::GetValue() const
662 if (mbFormatting
) //don't parse the entry if we're currently formatting what to put in it
665 return GetField() ? GetValueFromString(GetField()->GetText()) : 0;
668 sal_Int64
NumericFormatter::Normalize( sal_Int64 nValue
) const
670 return (nValue
* ImplPower10( GetDecimalDigits() ) );
673 sal_Int64
NumericFormatter::Denormalize( sal_Int64 nValue
) const
675 sal_Int64 nFactor
= ImplPower10( GetDecimalDigits() );
677 if ((nValue
< ( SAL_MIN_INT64
+ nFactor
)) ||
678 (nValue
> ( SAL_MAX_INT64
- nFactor
)))
680 return ( nValue
/ nFactor
);
685 sal_Int64 nHalf
= nFactor
/ 2;
686 return ((nValue
- nHalf
) / nFactor
);
690 sal_Int64 nHalf
= nFactor
/ 2;
691 return ((nValue
+ nHalf
) / nFactor
);
695 void NumericFormatter::Reformat()
700 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
703 ImplNumericReformat();
706 void NumericFormatter::FieldUp()
708 sal_Int64 nValue
= GetValue();
709 sal_Int64 nRemainder
= nValue
% mnSpinSize
;
711 nValue
= (nRemainder
== 0) ? nValue
+ mnSpinSize
: nValue
+ mnSpinSize
- nRemainder
;
713 nValue
= (nRemainder
== 0) ? nValue
+ mnSpinSize
: nValue
- nRemainder
;
715 nValue
= ClipAgainstMinMax(nValue
);
717 ImplNewFieldValue( nValue
);
720 void NumericFormatter::FieldDown()
722 sal_Int64 nValue
= GetValue();
723 sal_Int64 nRemainder
= nValue
% mnSpinSize
;
725 nValue
= (nRemainder
== 0) ? nValue
- mnSpinSize
: nValue
- nRemainder
;
727 nValue
= (nRemainder
== 0) ? nValue
- mnSpinSize
: nValue
- mnSpinSize
- nRemainder
;
729 nValue
= ClipAgainstMinMax(nValue
);
731 ImplNewFieldValue( nValue
);
734 void NumericFormatter::FieldFirst()
736 ImplNewFieldValue( mnFirst
);
739 void NumericFormatter::FieldLast()
741 ImplNewFieldValue( mnLast
);
744 void NumericFormatter::ImplNewFieldValue( sal_Int64 nNewValue
)
749 // !!! We should check why we do not validate in ImplSetUserValue() if the value was
750 // changed. This should be done there as well since otherwise the call to Modify would not
751 // be allowed. Anyway, the paths from ImplNewFieldValue, ImplSetUserValue, and ImplSetText
752 // should be checked and clearly traced (with comment) in order to find out what happens.
754 Selection aSelection
= GetField()->GetSelection();
755 aSelection
.Normalize();
756 OUString aText
= GetField()->GetText();
757 // leave it as is if selected until end
758 if ( static_cast<sal_Int32
>(aSelection
.Max()) == aText
.getLength() )
760 if ( !aSelection
.Len() )
761 aSelection
.Min() = SELECTION_MAX
;
762 aSelection
.Max() = SELECTION_MAX
;
765 sal_Int64 nOldLastValue
= mnLastValue
;
766 ImplSetUserValue( nNewValue
, &aSelection
);
767 mnLastValue
= nOldLastValue
;
769 // Modify during Edit is only set during KeyInput
770 if ( GetField()->GetText() != aText
)
772 GetField()->SetModifyFlag();
773 GetField()->Modify();
777 sal_Int64
NumericFormatter::ClipAgainstMinMax(sal_Int64 nValue
) const
781 else if (nValue
< mnMin
)
788 Size
calcMinimumSize(const Edit
&rSpinField
, const NumericFormatter
&rFormatter
)
793 nTextLen
= std::u16string_view(OUString::number(rFormatter
.GetMin())).size();
794 string::padToLength(aBuf
, nTextLen
, '9');
795 Size aMinTextSize
= rSpinField
.CalcMinimumSizeForText(
796 rFormatter
.CreateFieldText(OUString::unacquired(aBuf
).toInt64()));
799 nTextLen
= std::u16string_view(OUString::number(rFormatter
.GetMax())).size();
800 string::padToLength(aBuf
, nTextLen
, '9');
801 Size aMaxTextSize
= rSpinField
.CalcMinimumSizeForText(
802 rFormatter
.CreateFieldText(OUString::unacquired(aBuf
).toInt64()));
805 Size
aRet(std::max(aMinTextSize
.Width(), aMaxTextSize
.Width()),
806 std::max(aMinTextSize
.Height(), aMaxTextSize
.Height()));
808 OUStringBuffer
sBuf("999999999");
809 sal_uInt16 nDigits
= rFormatter
.GetDecimalDigits();
813 string::padToLength(aBuf
, aBuf
.getLength() + nDigits
, '9');
815 aMaxTextSize
= rSpinField
.CalcMinimumSizeForText(sBuf
.makeStringAndClear());
816 aRet
.setWidth( std::min(aRet
.Width(), aMaxTextSize
.Width()) );
822 NumericBox::NumericBox(vcl::Window
* pParent
, WinBits nWinStyle
)
823 : ComboBox(pParent
, nWinStyle
)
824 , NumericFormatter(this)
827 if ( !(nWinStyle
& WB_HIDE
) )
831 void NumericBox::dispose()
837 Size
NumericBox::CalcMinimumSize() const
839 Size
aRet(calcMinimumSize(*this, *this));
843 Size
aComboSugg(ComboBox::CalcMinimumSize());
844 aRet
.setWidth( std::max(aRet
.Width(), aComboSugg
.Width()) );
845 aRet
.setHeight( std::max(aRet
.Height(), aComboSugg
.Height()) );
851 bool NumericBox::PreNotify( NotifyEvent
& rNEvt
)
853 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
855 if ( ImplNumericProcessKeyInput( *rNEvt
.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
859 return ComboBox::PreNotify( rNEvt
);
862 bool NumericBox::EventNotify( NotifyEvent
& rNEvt
)
864 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
865 MarkToBeReformatted( false );
866 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
868 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
872 return ComboBox::EventNotify( rNEvt
);
875 void NumericBox::DataChanged( const DataChangedEvent
& rDCEvt
)
877 ComboBox::DataChanged( rDCEvt
);
879 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
881 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
882 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
883 ImplResetLocaleDataWrapper();
884 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
885 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
886 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
891 void NumericBox::Modify()
893 MarkToBeReformatted( true );
897 void NumericBox::ImplNumericReformat( const OUString
& rStr
, sal_Int64
& rValue
,
900 if (ImplNumericGetValue(rStr
, rValue
, GetDecimalDigits(), ImplGetLocaleDataWrapper()))
902 sal_Int64 nTempVal
= ClipAgainstMinMax(rValue
);
903 rOutStr
= CreateFieldText( nTempVal
);
907 void NumericBox::ReformatAll()
911 SetUpdateMode( false );
912 sal_Int32 nEntryCount
= GetEntryCount();
913 for ( sal_Int32 i
=0; i
< nEntryCount
; i
++ )
915 ImplNumericReformat( GetEntry( i
), nValue
, aStr
);
917 InsertEntry( aStr
, i
);
919 NumericFormatter::Reformat();
920 SetUpdateMode( true );
923 static bool ImplMetricProcessKeyInput( const KeyEvent
& rKEvt
,
924 bool bUseThousandSep
, const LocaleDataWrapper
& rWrapper
)
926 // no meaningful strict format; therefore allow all characters
927 return ImplNumericProcessKeyInput( rKEvt
, false, bUseThousandSep
, rWrapper
);
930 static OUString
ImplMetricGetUnitText(std::u16string_view rStr
)
934 for (sal_Int32 i
= static_cast<sal_Int32
>(rStr
.size())-1; i
>= 0; --i
)
936 sal_Unicode c
= rStr
[i
];
937 if ( (c
== '\'') || (c
== '\"') || (c
== '%') || (c
== 0x2032) || (c
== 0x2033) || unicode::isAlpha(c
) || unicode::isControl(c
) )
945 return aStr
.makeStringAndClear();
948 // #104355# support localized measurements
950 static OUString
ImplMetricToString( FieldUnit rUnit
)
952 // return unit's default string (ie, the first one )
953 for (auto const& elem
: ImplGetFieldUnits())
955 if (elem
.second
== rUnit
)
964 FieldUnit
StringToMetric(const OUString
&rMetricString
)
967 OUString aStr
= rMetricString
.toAsciiLowerCase().replaceAll(" ", "");
968 for (auto const& elem
: ImplGetCleanedFieldUnits())
970 if ( elem
.first
== aStr
)
974 return FieldUnit::NONE
;
978 static FieldUnit
ImplMetricGetUnit(std::u16string_view rStr
)
980 OUString aStr
= ImplMetricGetUnitText(rStr
);
981 return StringToMetric(aStr
);
984 static FieldUnit
ImplMap2FieldUnit( MapUnit meUnit
, tools::Long
& nDecDigits
)
988 case MapUnit::Map100thMM
:
990 return FieldUnit::MM
;
991 case MapUnit::Map10thMM
:
993 return FieldUnit::MM
;
994 case MapUnit::MapMM
:
995 return FieldUnit::MM
;
996 case MapUnit::MapCM
:
997 return FieldUnit::CM
;
998 case MapUnit::Map1000thInch
:
1000 return FieldUnit::INCH
;
1001 case MapUnit::Map100thInch
:
1003 return FieldUnit::INCH
;
1004 case MapUnit::Map10thInch
:
1006 return FieldUnit::INCH
;
1007 case MapUnit::MapInch
:
1008 return FieldUnit::INCH
;
1009 case MapUnit::MapPoint
:
1010 return FieldUnit::POINT
;
1011 case MapUnit::MapTwip
:
1012 return FieldUnit::TWIP
;
1014 OSL_FAIL( "default eInUnit" );
1017 return FieldUnit::NONE
;
1020 static double nonValueDoubleToValueDouble( double nValue
)
1022 return std::isfinite( nValue
) ? nValue
: 0.0;
1027 sal_Int64
ConvertValue(sal_Int64 nValue
, sal_Int64 mnBaseValue
, sal_uInt16 nDecDigits
,
1028 FieldUnit eInUnit
, FieldUnit eOutUnit
)
1030 double nDouble
= nonValueDoubleToValueDouble(vcl::ConvertDoubleValue(
1031 static_cast<double>(nValue
), mnBaseValue
, nDecDigits
, eInUnit
, eOutUnit
));
1034 // caution: precision loss in double cast
1035 if ( nDouble
<= double(SAL_MIN_INT64
) )
1036 nLong
= SAL_MIN_INT64
;
1037 else if ( nDouble
>= double(SAL_MAX_INT64
) )
1038 nLong
= SAL_MAX_INT64
;
1040 nLong
= static_cast<sal_Int64
>( std::round(nDouble
) );
1048 bool checkConversionUnits(MapUnit eInUnit
, FieldUnit eOutUnit
)
1050 return eOutUnit
!= FieldUnit::PERCENT
1051 && eOutUnit
!= FieldUnit::CUSTOM
1052 && eOutUnit
!= FieldUnit::NONE
1053 && eInUnit
!= MapUnit::MapPixel
1054 && eInUnit
!= MapUnit::MapSysFont
1055 && eInUnit
!= MapUnit::MapAppFont
1056 && eInUnit
!= MapUnit::MapRelative
;
1059 double convertValue( double nValue
, tools::Long nDigits
, FieldUnit eInUnit
, FieldUnit eOutUnit
)
1072 nValue
*= ImplPower10(nDigits
);
1075 if ( eInUnit
!= eOutUnit
)
1077 const o3tl::Length eFrom
= FieldToO3tlLength(eInUnit
), eTo
= FieldToO3tlLength(eOutUnit
);
1078 if (eFrom
!= o3tl::Length::invalid
&& eTo
!= o3tl::Length::invalid
)
1079 nValue
= o3tl::convert(nValue
, eFrom
, eTo
);
1089 sal_Int64
ConvertValue( sal_Int64 nValue
, sal_uInt16 nDigits
,
1090 MapUnit eInUnit
, FieldUnit eOutUnit
)
1092 if ( !checkConversionUnits(eInUnit
, eOutUnit
) )
1094 OSL_FAIL( "invalid parameters" );
1098 tools::Long nDecDigits
= nDigits
;
1099 FieldUnit eFieldUnit
= ImplMap2FieldUnit( eInUnit
, nDecDigits
);
1101 // Avoid sal_Int64 <-> double conversion issues if possible:
1102 if (eFieldUnit
== eOutUnit
&& nDigits
== 0)
1107 return static_cast<sal_Int64
>(
1108 nonValueDoubleToValueDouble(
1109 convertValue( nValue
, nDecDigits
, eFieldUnit
, eOutUnit
) ) );
1112 double ConvertDoubleValue(double nValue
, sal_Int64 mnBaseValue
, sal_uInt16 nDecDigits
,
1113 FieldUnit eInUnit
, FieldUnit eOutUnit
)
1115 if ( eInUnit
!= eOutUnit
)
1117 if (eInUnit
== FieldUnit::PERCENT
&& mnBaseValue
> 0 && nValue
> 0)
1119 sal_Int64 nDiv
= 100 * ImplPower10(nDecDigits
);
1121 if (mnBaseValue
!= 1)
1122 nValue
*= mnBaseValue
;
1129 const o3tl::Length eFrom
= FieldToO3tlLength(eInUnit
, o3tl::Length::invalid
);
1130 const o3tl::Length eTo
= FieldToO3tlLength(eOutUnit
, o3tl::Length::invalid
);
1131 if (eFrom
!= o3tl::Length::invalid
&& eTo
!= o3tl::Length::invalid
)
1132 nValue
= o3tl::convert(nValue
, eFrom
, eTo
);
1139 double ConvertDoubleValue(double nValue
, sal_uInt16 nDigits
,
1140 MapUnit eInUnit
, FieldUnit eOutUnit
)
1142 if ( !checkConversionUnits(eInUnit
, eOutUnit
) )
1144 OSL_FAIL( "invalid parameters" );
1148 tools::Long nDecDigits
= nDigits
;
1149 FieldUnit eFieldUnit
= ImplMap2FieldUnit( eInUnit
, nDecDigits
);
1151 return convertValue(nValue
, nDecDigits
, eFieldUnit
, eOutUnit
);
1154 double ConvertDoubleValue(double nValue
, sal_uInt16 nDigits
,
1155 FieldUnit eInUnit
, MapUnit eOutUnit
)
1157 if ( eInUnit
== FieldUnit::PERCENT
||
1158 eInUnit
== FieldUnit::CUSTOM
||
1159 eInUnit
== FieldUnit::NONE
||
1160 eInUnit
== FieldUnit::DEGREE
||
1161 eInUnit
== FieldUnit::SECOND
||
1162 eInUnit
== FieldUnit::MILLISECOND
||
1163 eInUnit
== FieldUnit::PIXEL
||
1164 eOutUnit
== MapUnit::MapPixel
||
1165 eOutUnit
== MapUnit::MapSysFont
||
1166 eOutUnit
== MapUnit::MapAppFont
||
1167 eOutUnit
== MapUnit::MapRelative
)
1169 OSL_FAIL( "invalid parameters" );
1173 tools::Long nDecDigits
= nDigits
;
1174 FieldUnit eFieldUnit
= ImplMap2FieldUnit( eOutUnit
, nDecDigits
);
1176 if ( nDecDigits
< 0 )
1178 nValue
*= ImplPower10(-nDecDigits
);
1182 nValue
/= ImplPower10(nDecDigits
);
1185 if ( eFieldUnit
!= eInUnit
)
1187 const o3tl::Length eFrom
= FieldToO3tlLength(eInUnit
, o3tl::Length::invalid
);
1188 const o3tl::Length eTo
= FieldToO3tlLength(eFieldUnit
, o3tl::Length::invalid
);
1189 if (eFrom
!= o3tl::Length::invalid
&& eTo
!= o3tl::Length::invalid
)
1190 nValue
= o3tl::convert(nValue
, eFrom
, eTo
);
1198 bool TextToValue(const OUString
& rStr
, double& rValue
, sal_Int64 nBaseValue
,
1199 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
, FieldUnit eUnit
)
1203 if ( !ImplNumericGetValue( rStr
, nValue
, nDecDigits
, rLocaleDataWrapper
) )
1207 FieldUnit eEntryUnit
= ImplMetricGetUnit( rStr
);
1210 // caution: conversion to double loses precision
1211 rValue
= vcl::ConvertDoubleValue(static_cast<double>(nValue
), nBaseValue
, nDecDigits
, eEntryUnit
, eUnit
);
1217 void MetricFormatter::ImplMetricReformat( const OUString
& rStr
, double& rValue
, OUString
& rOutStr
)
1219 if (!vcl::TextToValue(rStr
, rValue
, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit
))
1222 double nTempVal
= rValue
;
1223 // caution: precision loss in double cast
1224 if ( nTempVal
> GetMax() )
1225 nTempVal
= static_cast<double>(GetMax());
1226 else if ( nTempVal
< GetMin())
1227 nTempVal
= static_cast<double>(GetMin());
1228 rOutStr
= CreateFieldText( static_cast<sal_Int64
>(nTempVal
) );
1231 MetricFormatter::MetricFormatter(Edit
* pEdit
)
1232 : NumericFormatter(pEdit
)
1233 , meUnit(FieldUnit::NONE
)
1237 MetricFormatter::~MetricFormatter()
1241 void MetricFormatter::SetUnit( FieldUnit eNewUnit
)
1243 if (eNewUnit
== FieldUnit::MM_100TH
)
1245 SetDecimalDigits( GetDecimalDigits() + 2 );
1246 meUnit
= FieldUnit::MM
;
1253 void MetricFormatter::SetCustomUnitText( const OUString
& rStr
)
1255 maCustomUnitText
= rStr
;
1259 void MetricFormatter::SetValue( sal_Int64 nNewValue
, FieldUnit eInUnit
)
1261 SetUserValue( nNewValue
, eInUnit
);
1264 OUString
MetricFormatter::CreateFieldText( sal_Int64 nValue
) const
1266 //whether percent is separated from its number is locale
1267 //specific, pawn it off to icu to decide
1268 if (meUnit
== FieldUnit::PERCENT
)
1270 double dValue
= nValue
;
1271 dValue
/= ImplPower10(GetDecimalDigits());
1272 return unicode::formatPercent(dValue
, GetLanguageTag());
1275 OUString aStr
= NumericFormatter::CreateFieldText( nValue
);
1277 if( meUnit
== FieldUnit::CUSTOM
)
1278 aStr
+= maCustomUnitText
;
1281 OUString aSuffix
= ImplMetricToString( meUnit
);
1282 if (meUnit
!= FieldUnit::NONE
&& meUnit
!= FieldUnit::DEGREE
&& meUnit
!= FieldUnit::INCH
&& meUnit
!= FieldUnit::FOOT
)
1284 if (meUnit
== FieldUnit::INCH
)
1286 OUString sDoublePrime
= u
"\u2033";
1287 if (aSuffix
!= "\"" && aSuffix
!= sDoublePrime
)
1290 aSuffix
= sDoublePrime
;
1292 else if (meUnit
== FieldUnit::FOOT
)
1294 OUString sPrime
= u
"\u2032";
1295 if (aSuffix
!= "'" && aSuffix
!= sPrime
)
1301 assert(meUnit
!= FieldUnit::PERCENT
);
1307 void MetricFormatter::SetUserValue( sal_Int64 nNewValue
, FieldUnit eInUnit
)
1309 // convert to previously configured units
1310 nNewValue
= vcl::ConvertValue( nNewValue
, 0, GetDecimalDigits(), eInUnit
, meUnit
);
1311 NumericFormatter::SetUserValue( nNewValue
);
1314 sal_Int64
MetricFormatter::GetValueFromStringUnit(const OUString
& rStr
, FieldUnit eOutUnit
) const
1317 // caution: precision loss in double cast
1318 if (!vcl::TextToValue(rStr
, nTempValue
, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit
))
1319 nTempValue
= static_cast<double>(mnLastValue
);
1321 // caution: precision loss in double cast
1322 if (nTempValue
> mnMax
)
1323 nTempValue
= static_cast<double>(mnMax
);
1324 else if (nTempValue
< mnMin
)
1325 nTempValue
= static_cast<double>(mnMin
);
1327 // convert to requested units
1328 return vcl::ConvertValue(static_cast<sal_Int64
>(nTempValue
), 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1331 sal_Int64
MetricFormatter::GetValueFromString(const OUString
& rStr
) const
1333 return GetValueFromStringUnit(rStr
, FieldUnit::NONE
);
1336 sal_Int64
MetricFormatter::GetValue( FieldUnit eOutUnit
) const
1338 return GetField() ? GetValueFromStringUnit(GetField()->GetText(), eOutUnit
) : 0;
1341 void MetricFormatter::SetValue( sal_Int64 nValue
)
1343 // Implementation not inline, because it is a virtual Function
1344 SetValue( nValue
, FieldUnit::NONE
);
1347 void MetricFormatter::SetMin( sal_Int64 nNewMin
, FieldUnit eInUnit
)
1349 // convert to requested units
1350 NumericFormatter::SetMin(vcl::ConvertValue(nNewMin
, 0, GetDecimalDigits(), eInUnit
, meUnit
));
1353 sal_Int64
MetricFormatter::GetMin( FieldUnit eOutUnit
) const
1355 // convert to requested units
1356 return vcl::ConvertValue(NumericFormatter::GetMin(), 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1359 void MetricFormatter::SetMax( sal_Int64 nNewMax
, FieldUnit eInUnit
)
1361 // convert to requested units
1362 NumericFormatter::SetMax(vcl::ConvertValue(nNewMax
, 0, GetDecimalDigits(), eInUnit
, meUnit
));
1365 sal_Int64
MetricFormatter::GetMax( FieldUnit eOutUnit
) const
1367 // convert to requested units
1368 return vcl::ConvertValue(NumericFormatter::GetMax(), 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1371 void MetricFormatter::Reformat()
1376 OUString aText
= GetField()->GetText();
1379 // caution: precision loss in double cast
1380 double nTemp
= static_cast<double>(mnLastValue
);
1381 ImplMetricReformat( aText
, nTemp
, aStr
);
1382 mnLastValue
= static_cast<sal_Int64
>(nTemp
);
1384 if ( !aStr
.isEmpty() )
1386 ImplSetText( aStr
);
1389 SetValue( mnLastValue
);
1392 sal_Int64
MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit
) const
1394 // convert to requested units
1395 return vcl::ConvertValue(0/*nCorrectedValue*/, 0, GetDecimalDigits(),
1399 MetricField::MetricField(vcl::Window
* pParent
, WinBits nWinStyle
)
1400 : SpinField(pParent
, nWinStyle
, WindowType::METRICFIELD
)
1401 , MetricFormatter(this)
1406 void MetricField::dispose()
1409 SpinField::dispose();
1412 Size
MetricField::CalcMinimumSize() const
1414 return calcMinimumSize(*this, *this);
1417 bool MetricField::set_property(const OUString
&rKey
, const OUString
&rValue
)
1419 if (rKey
== "digits")
1420 SetDecimalDigits(rValue
.toInt32());
1421 else if (rKey
== "spin-size")
1422 SetSpinSize(rValue
.toInt32());
1424 return SpinField::set_property(rKey
, rValue
);
1428 void MetricField::SetUnit( FieldUnit nNewUnit
)
1430 sal_Int64 nRawMax
= GetMax( nNewUnit
);
1431 sal_Int64 nMax
= Denormalize( nRawMax
);
1432 sal_Int64 nMin
= Denormalize( GetMin( nNewUnit
) );
1433 sal_Int64 nFirst
= Denormalize( GetFirst( nNewUnit
) );
1434 sal_Int64 nLast
= Denormalize( GetLast( nNewUnit
) );
1436 MetricFormatter::SetUnit( nNewUnit
);
1438 SetMax( Normalize( nMax
), nNewUnit
);
1439 SetMin( Normalize( nMin
), nNewUnit
);
1440 SetFirst( Normalize( nFirst
), nNewUnit
);
1441 SetLast( Normalize( nLast
), nNewUnit
);
1444 void MetricField::SetFirst( sal_Int64 nNewFirst
, FieldUnit eInUnit
)
1447 nNewFirst
= vcl::ConvertValue(nNewFirst
, 0, GetDecimalDigits(), eInUnit
, meUnit
);
1448 mnFirst
= nNewFirst
;
1451 sal_Int64
MetricField::GetFirst( FieldUnit eOutUnit
) const
1454 return vcl::ConvertValue(mnFirst
, 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1457 void MetricField::SetLast( sal_Int64 nNewLast
, FieldUnit eInUnit
)
1460 nNewLast
= vcl::ConvertValue(nNewLast
, 0, GetDecimalDigits(), eInUnit
, meUnit
);
1464 sal_Int64
MetricField::GetLast( FieldUnit eOutUnit
) const
1467 return vcl::ConvertValue(mnLast
, 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1470 bool MetricField::PreNotify( NotifyEvent
& rNEvt
)
1472 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
1474 if ( ImplMetricProcessKeyInput( *rNEvt
.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1478 return SpinField::PreNotify( rNEvt
);
1481 bool MetricField::EventNotify( NotifyEvent
& rNEvt
)
1483 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
1484 MarkToBeReformatted( false );
1485 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
1487 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1491 return SpinField::EventNotify( rNEvt
);
1494 void MetricField::DataChanged( const DataChangedEvent
& rDCEvt
)
1496 SpinField::DataChanged( rDCEvt
);
1498 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
1500 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1501 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1502 ImplResetLocaleDataWrapper();
1503 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1504 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1505 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
1510 void MetricField::Modify()
1512 MarkToBeReformatted( true );
1513 SpinField::Modify();
1516 void MetricField::Up()
1522 void MetricField::Down()
1528 void MetricField::First()
1534 void MetricField::Last()
1540 void MetricField::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
)
1542 SpinField::DumpAsPropertyTree(rJsonWriter
);
1543 rJsonWriter
.put("min", GetMin());
1544 rJsonWriter
.put("max", GetMax());
1545 rJsonWriter
.put("unit", FieldUnitToString(GetUnit()));
1546 OUString sValue
= Application::GetSettings().GetNeutralLocaleDataWrapper().
1547 getNum(GetValue(), GetDecimalDigits(), false, false);
1548 rJsonWriter
.put("value", sValue
);
1551 FactoryFunction
MetricField::GetUITestFactory() const
1553 return MetricFieldUIObject::create
;
1556 MetricBox::MetricBox(vcl::Window
* pParent
, WinBits nWinStyle
)
1557 : ComboBox(pParent
, nWinStyle
)
1558 , MetricFormatter(this)
1563 void MetricBox::dispose()
1566 ComboBox::dispose();
1569 Size
MetricBox::CalcMinimumSize() const
1571 Size
aRet(calcMinimumSize(*this, *this));
1573 if (IsDropDownBox())
1575 Size
aComboSugg(ComboBox::CalcMinimumSize());
1576 aRet
.setWidth( std::max(aRet
.Width(), aComboSugg
.Width()) );
1577 aRet
.setHeight( std::max(aRet
.Height(), aComboSugg
.Height()) );
1583 bool MetricBox::PreNotify( NotifyEvent
& rNEvt
)
1585 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
1587 if ( ImplMetricProcessKeyInput( *rNEvt
.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1591 return ComboBox::PreNotify( rNEvt
);
1594 bool MetricBox::EventNotify( NotifyEvent
& rNEvt
)
1596 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
1597 MarkToBeReformatted( false );
1598 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
1600 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1604 return ComboBox::EventNotify( rNEvt
);
1607 void MetricBox::DataChanged( const DataChangedEvent
& rDCEvt
)
1609 ComboBox::DataChanged( rDCEvt
);
1611 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
1613 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1614 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1615 ImplResetLocaleDataWrapper();
1616 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1617 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1618 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
1623 void MetricBox::Modify()
1625 MarkToBeReformatted( true );
1629 void MetricBox::ReformatAll()
1633 SetUpdateMode( false );
1634 sal_Int32 nEntryCount
= GetEntryCount();
1635 for ( sal_Int32 i
=0; i
< nEntryCount
; i
++ )
1637 ImplMetricReformat( GetEntry( i
), nValue
, aStr
);
1639 InsertEntry( aStr
, i
);
1641 MetricFormatter::Reformat();
1642 SetUpdateMode( true );
1645 static bool ImplCurrencyProcessKeyInput( const KeyEvent
& rKEvt
,
1646 bool bUseThousandSep
, const LocaleDataWrapper
& rWrapper
)
1648 // no strict format set; therefore allow all characters
1649 return ImplNumericProcessKeyInput( rKEvt
, false, bUseThousandSep
, rWrapper
);
1652 static bool ImplCurrencyGetValue( const OUString
& rStr
, sal_Int64
& rValue
,
1653 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rWrapper
)
1656 return ImplNumericGetValue( rStr
, rValue
, nDecDigits
, rWrapper
, true );
1659 void CurrencyFormatter::ImplCurrencyReformat( const OUString
& rStr
, OUString
& rOutStr
)
1662 if ( !ImplNumericGetValue( rStr
, nValue
, GetDecimalDigits(), ImplGetLocaleDataWrapper(), true ) )
1665 sal_Int64 nTempVal
= nValue
;
1666 if ( nTempVal
> GetMax() )
1667 nTempVal
= GetMax();
1668 else if ( nTempVal
< GetMin())
1669 nTempVal
= GetMin();
1670 rOutStr
= CreateFieldText( nTempVal
);
1673 CurrencyFormatter::CurrencyFormatter(Edit
* pField
)
1674 : NumericFormatter(pField
)
1678 CurrencyFormatter::~CurrencyFormatter()
1682 void CurrencyFormatter::SetValue( sal_Int64 nNewValue
)
1684 SetUserValue( nNewValue
);
1685 SetEmptyFieldValueData( false );
1688 OUString
CurrencyFormatter::CreateFieldText( sal_Int64 nValue
) const
1690 return ImplGetLocaleDataWrapper().getCurr( nValue
, GetDecimalDigits(),
1691 ImplGetLocaleDataWrapper().getCurrSymbol(),
1692 IsUseThousandSep() );
1695 sal_Int64
CurrencyFormatter::GetValueFromString(const OUString
& rStr
) const
1697 sal_Int64 nTempValue
;
1698 if ( ImplCurrencyGetValue( rStr
, nTempValue
, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
1700 return ClipAgainstMinMax(nTempValue
);
1706 void CurrencyFormatter::Reformat()
1712 ImplCurrencyReformat( GetField()->GetText(), aStr
);
1714 if ( !aStr
.isEmpty() )
1716 ImplSetText( aStr
);
1717 sal_Int64 nTemp
= mnLastValue
;
1718 ImplCurrencyGetValue( aStr
, nTemp
, GetDecimalDigits(), ImplGetLocaleDataWrapper() );
1719 mnLastValue
= nTemp
;
1722 SetValue( mnLastValue
);
1725 CurrencyField::CurrencyField(vcl::Window
* pParent
, WinBits nWinStyle
)
1726 : SpinField(pParent
, nWinStyle
)
1727 , CurrencyFormatter(this)
1732 void CurrencyField::dispose()
1735 SpinField::dispose();
1738 bool CurrencyField::PreNotify( NotifyEvent
& rNEvt
)
1740 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
1742 if ( ImplCurrencyProcessKeyInput( *rNEvt
.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1746 return SpinField::PreNotify( rNEvt
);
1749 bool CurrencyField::EventNotify( NotifyEvent
& rNEvt
)
1751 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
1752 MarkToBeReformatted( false );
1753 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
1755 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1759 return SpinField::EventNotify( rNEvt
);
1762 void CurrencyField::DataChanged( const DataChangedEvent
& rDCEvt
)
1764 SpinField::DataChanged( rDCEvt
);
1766 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
1768 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1769 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1770 ImplResetLocaleDataWrapper();
1771 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1772 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1773 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
1778 void CurrencyField::Modify()
1780 MarkToBeReformatted( true );
1781 SpinField::Modify();
1784 void CurrencyField::Up()
1790 void CurrencyField::Down()
1796 void CurrencyField::First()
1802 void CurrencyField::Last()
1808 CurrencyBox::CurrencyBox(vcl::Window
* pParent
, WinBits nWinStyle
)
1809 : ComboBox(pParent
, nWinStyle
)
1810 , CurrencyFormatter(this)
1815 void CurrencyBox::dispose()
1818 ComboBox::dispose();
1821 bool CurrencyBox::PreNotify( NotifyEvent
& rNEvt
)
1823 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
1825 if ( ImplCurrencyProcessKeyInput( *rNEvt
.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1829 return ComboBox::PreNotify( rNEvt
);
1832 bool CurrencyBox::EventNotify( NotifyEvent
& rNEvt
)
1834 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
1835 MarkToBeReformatted( false );
1836 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
1838 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1842 return ComboBox::EventNotify( rNEvt
);
1845 void CurrencyBox::DataChanged( const DataChangedEvent
& rDCEvt
)
1847 ComboBox::DataChanged( rDCEvt
);
1849 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
1851 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1852 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1853 ImplResetLocaleDataWrapper();
1854 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1855 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1856 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
1861 void CurrencyBox::Modify()
1863 MarkToBeReformatted( true );
1867 void CurrencyBox::ReformatAll()
1870 SetUpdateMode( false );
1871 sal_Int32 nEntryCount
= GetEntryCount();
1872 for ( sal_Int32 i
=0; i
< nEntryCount
; i
++ )
1874 ImplCurrencyReformat( GetEntry( i
), aStr
);
1876 InsertEntry( aStr
, i
);
1878 CurrencyFormatter::Reformat();
1879 SetUpdateMode( true );
1882 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */