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";
121 case FieldUnit::FONT_EM
:
124 case FieldUnit::FONT_CJK_ADVANCE
:
131 sal_Int64
ImplPower10( sal_uInt16 n
)
134 sal_Int64 nValue
= 1;
136 for ( i
=0; i
< n
; i
++ )
142 bool ImplNumericProcessKeyInput( const KeyEvent
& rKEvt
,
143 bool bStrictFormat
, bool bThousandSep
,
144 const LocaleDataWrapper
& rLocaleDataWrapper
)
146 if ( !bStrictFormat
)
150 sal_Unicode cChar
= rKEvt
.GetCharCode();
151 sal_uInt16 nGroup
= rKEvt
.GetKeyCode().GetGroup();
153 return !((nGroup
== KEYGROUP_FKEYS
) ||
154 (nGroup
== KEYGROUP_CURSOR
) ||
155 (nGroup
== KEYGROUP_MISC
) ||
156 ((cChar
>= '0') && (cChar
<= '9')) ||
157 rLocaleDataWrapper
.getNumDecimalSep() == OUStringChar(cChar
) ||
158 (bThousandSep
&& rLocaleDataWrapper
.getNumThousandSep() == OUStringChar(cChar
)) ||
159 rLocaleDataWrapper
.getNumDecimalSepAlt() == OUStringChar(cChar
) ||
164 bool ImplNumericGetValue( const OUString
& rStr
, sal_Int64
& rValue
,
165 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
,
166 bool bCurrency
= false )
168 OUString aStr
= rStr
;
169 OUStringBuffer aStr1
, aStr2
, aStrNum
, aStrDenom
;
170 bool bNegative
= false;
172 sal_Int32 nDecPos
, nFracDivPos
;
175 // react on empty string
176 if ( rStr
.isEmpty() )
179 // remove leading and trailing spaces
183 // find position of decimal point
184 nDecPos
= aStr
.indexOf( rLocaleDataWrapper
.getNumDecimalSep() );
185 if (nDecPos
< 0 && !rLocaleDataWrapper
.getNumDecimalSepAlt().isEmpty())
186 nDecPos
= aStr
.indexOf( rLocaleDataWrapper
.getNumDecimalSepAlt() );
187 // find position of fraction
188 nFracDivPos
= aStr
.indexOf( '/' );
190 // parse fractional strings
194 sal_Int32 nFracNumPos
= aStr
.lastIndexOf(' ', nFracDivPos
);
196 // If in "a b/c" format.
197 if(nFracNumPos
!= -1 )
199 aStr1
.append(aStr
.subView(0, nFracNumPos
));
200 aStrNum
.append(aStr
.subView(nFracNumPos
+1, nFracDivPos
-nFracNumPos
-1));
201 aStrDenom
.append(aStr
.subView(nFracDivPos
+1));
203 // "a/b" format, or not a fraction at all
206 aStrNum
.append(aStr
.subView(0, nFracDivPos
));
207 aStrDenom
.append(aStr
.subView(nFracDivPos
+1));
211 // parse decimal strings
212 else if ( nDecPos
>= 0)
214 aStr1
.append(aStr
.subView(0, nDecPos
));
215 aStr2
.append(aStr
.subView(nDecPos
+1));
223 if ( aStr
.startsWith("(") && aStr
.endsWith(")") )
227 for (sal_Int32 i
=0; i
< aStr
.getLength(); i
++ )
229 if ( (aStr
[i
] >= '0') && (aStr
[i
] <= '9') )
231 else if ( aStr
[i
] == '-' )
238 if (!bNegative
&& !aStr
.isEmpty())
240 sal_uInt16 nFormat
= rLocaleDataWrapper
.getCurrNegativeFormat();
241 if ( (nFormat
== 3) || (nFormat
== 6) || // $1- || 1-$
242 (nFormat
== 7) || (nFormat
== 10) ) // 1$- || 1 $-
244 for (sal_Int32 i
= aStr
.getLength()-1; i
> 0; --i
)
246 if ( (aStr
[i
] >= '0') && (aStr
[i
] <= '9') )
248 else if ( aStr
[i
] == '-' )
259 if ( !aStr1
.isEmpty() && aStr1
[0] == '-')
261 if ( !aStrNum
.isEmpty() && aStrNum
[0] == '-') // For non-mixed fractions
265 // remove all unwanted characters
267 for (sal_Int32 i
=0; i
< aStr1
.getLength(); )
269 if ( (aStr1
[i
] >= '0') && (aStr1
[i
] <= '9') )
272 aStr1
.remove( i
, 1 );
276 for (sal_Int32 i
=0; i
< aStr2
.getLength(); )
278 if ((aStr2
[i
] >= '0') && (aStr2
[i
] <= '9'))
286 for (sal_Int32 i
=0; i
< aStrNum
.getLength(); )
288 if ((aStrNum
[i
] >= '0') && (aStrNum
[i
] <= '9'))
291 aStrNum
.remove(i
, 1);
294 for (sal_Int32 i
=0; i
< aStrDenom
.getLength(); )
296 if ((aStrDenom
[i
] >= '0') && (aStrDenom
[i
] <= '9'))
299 aStrDenom
.remove(i
, 1);
304 if ( !bFrac
&& aStr1
.isEmpty() && aStr2
.isEmpty() )
306 else if ( bFrac
&& aStr1
.isEmpty() && (aStrNum
.isEmpty() || aStrDenom
.isEmpty()) )
309 if ( aStr1
.isEmpty() )
312 aStr1
.insert(0, "-");
314 // Convert fractional strings
316 // Convert to fraction
317 sal_Int64 nWholeNum
= o3tl::toInt64(aStr1
);
319 sal_Int64 nNum
= o3tl::toInt64(aStrNum
);
320 sal_Int64 nDenom
= o3tl::toInt64(aStrDenom
);
321 if (nDenom
== 0) return false; // Division by zero
322 double nFrac2Dec
= nWholeNum
+ static_cast<double>(nNum
)/nDenom
; // Convert to double for floating point precision
323 OUStringBuffer
aStrFrac(OUString::number(nFrac2Dec
));
324 // Reconvert division result to string and parse
325 nDecPos
= aStrFrac
.indexOf('.');
328 aStr1
.append(aStrFrac
.getStr(), nDecPos
);
329 aStr2
.append(aStrFrac
.getStr()+nDecPos
+1);
332 aStr1
= std::move(aStrFrac
);
335 // prune and round fraction
337 if (aStr2
.getLength() > nDecDigits
)
339 if (aStr2
[nDecDigits
] >= '5')
341 string::truncateToLength(aStr2
, nDecDigits
);
343 if (aStr2
.getLength() < nDecDigits
)
344 string::padToLength(aStr2
, nDecDigits
, '0');
346 aStr
= aStr1
+ aStr2
;
349 nValue
= aStr
.toInt64();
352 // check if string is equivalent to zero
353 sal_Int32 nIndex
= bNegative
? 1 : 0;
354 while (nIndex
< aStr
.getLength() && aStr
[nIndex
] == '0')
356 if( nIndex
< aStr
.getLength() )
358 rValue
= bNegative
? SAL_MIN_INT64
: SAL_MAX_INT64
;
375 void ImplUpdateSeparatorString( OUString
& io_rText
,
376 std::u16string_view rOldDecSep
, std::u16string_view rNewDecSep
,
377 std::u16string_view rOldThSep
, std::u16string_view rNewThSep
)
379 OUStringBuffer
aBuf( io_rText
.getLength() );
380 sal_Int32 nIndexDec
= 0, nIndexTh
= 0, nIndex
= 0;
382 const sal_Unicode
* pBuffer
= io_rText
.getStr();
383 while( nIndex
!= -1 )
385 nIndexDec
= io_rText
.indexOf( rOldDecSep
, nIndex
);
386 nIndexTh
= io_rText
.indexOf( rOldThSep
, nIndex
);
387 if( (nIndexTh
!= -1 && nIndexDec
!= -1 && nIndexTh
< nIndexDec
)
388 || (nIndexTh
!= -1 && nIndexDec
== -1)
391 aBuf
.append( OUString::Concat(std::u16string_view(pBuffer
+ nIndex
, nIndexTh
- nIndex
)) + rNewThSep
);
392 nIndex
= nIndexTh
+ rOldThSep
.size();
394 else if( nIndexDec
!= -1 )
396 aBuf
.append( OUString::Concat(std::u16string_view(pBuffer
+ nIndex
, nIndexDec
- nIndex
)) + rNewDecSep
);
397 nIndex
= nIndexDec
+ rOldDecSep
.size();
401 aBuf
.append( pBuffer
+ nIndex
);
406 io_rText
= aBuf
.makeStringAndClear();
409 void ImplUpdateSeparators( std::u16string_view rOldDecSep
, std::u16string_view rNewDecSep
,
410 std::u16string_view rOldThSep
, std::u16string_view rNewThSep
,
413 bool bChangeDec
= (rOldDecSep
!= rNewDecSep
);
414 bool bChangeTh
= (rOldThSep
!= rNewThSep
);
416 if( !(bChangeDec
|| bChangeTh
) )
419 bool bUpdateMode
= pEdit
->IsUpdateMode();
420 pEdit
->SetUpdateMode( false );
421 OUString aText
= pEdit
->GetText();
422 ImplUpdateSeparatorString( aText
, rOldDecSep
, rNewDecSep
, rOldThSep
, rNewThSep
);
423 pEdit
->SetText( aText
);
425 ComboBox
* pCombo
= dynamic_cast<ComboBox
*>(pEdit
);
428 // update box entries
429 sal_Int32 nEntryCount
= pCombo
->GetEntryCount();
430 for ( sal_Int32 i
=0; i
< nEntryCount
; i
++ )
432 aText
= pCombo
->GetEntry( i
);
433 void* pEntryData
= pCombo
->GetEntryData( i
);
434 ImplUpdateSeparatorString( aText
, rOldDecSep
, rNewDecSep
, rOldThSep
, rNewThSep
);
435 pCombo
->RemoveEntryAt(i
);
436 pCombo
->InsertEntry( aText
, i
);
437 pCombo
->SetEntryData( i
, pEntryData
);
441 pEdit
->SetUpdateMode( bUpdateMode
);
446 FormatterBase::FormatterBase(Edit
* pField
)
449 mpLocaleDataWrapper
= nullptr;
451 mbStrictFormat
= false;
452 mbEmptyFieldValue
= false;
453 mbEmptyFieldValueEnabled
= false;
456 FormatterBase::~FormatterBase()
460 LocaleDataWrapper
& FormatterBase::ImplGetLocaleDataWrapper() const
462 if ( !mpLocaleDataWrapper
)
464 mpLocaleDataWrapper
.reset( new LocaleDataWrapper( GetLanguageTag() ) );
466 return *mpLocaleDataWrapper
;
469 /** reset the LocaleDataWrapper when the language tag changes */
470 void FormatterBase::ImplResetLocaleDataWrapper() const
472 // just get rid of, the next time it is requested, it will get loaded with the right
474 mpLocaleDataWrapper
.reset();
477 const LocaleDataWrapper
& FormatterBase::GetLocaleDataWrapper() const
479 return ImplGetLocaleDataWrapper();
482 void FormatterBase::Reformat()
486 void FormatterBase::ReformatAll()
491 void FormatterBase::SetStrictFormat( bool bStrict
)
493 if ( bStrict
!= mbStrictFormat
)
495 mbStrictFormat
= bStrict
;
496 if ( mbStrictFormat
)
501 const lang::Locale
& FormatterBase::GetLocale() const
504 return mpField
->GetSettings().GetLanguageTag().getLocale();
506 return Application::GetSettings().GetLanguageTag().getLocale();
509 const LanguageTag
& FormatterBase::GetLanguageTag() const
512 return mpField
->GetSettings().GetLanguageTag();
514 return Application::GetSettings().GetLanguageTag();
517 void FormatterBase::ImplSetText( const OUString
& rText
, Selection
const * pNewSelection
)
522 mpField
->SetText(rText
, *pNewSelection
);
525 Selection aSel
= mpField
->GetSelection();
526 aSel
.Min() = aSel
.Max();
527 mpField
->SetText(rText
, aSel
);
529 MarkToBeReformatted( false );
533 void FormatterBase::SetEmptyFieldValue()
536 mpField
->SetText( OUString() );
537 mbEmptyFieldValue
= true;
540 bool FormatterBase::IsEmptyFieldValue() const
542 return (!mpField
|| mpField
->GetText().isEmpty());
545 void NumericFormatter::FormatValue(Selection
const * pNewSelection
)
548 ImplSetText(CreateFieldText(mnLastValue
), pNewSelection
);
549 mbFormatting
= false;
552 void NumericFormatter::ImplNumericReformat()
554 mnLastValue
= GetValue();
558 NumericFormatter::NumericFormatter(Edit
* pEdit
)
559 : FormatterBase(pEdit
)
562 // a "large" value substantially smaller than SAL_MAX_INT64, to avoid
563 // overflow in computations using this "dummy" value
564 , mnMax(SAL_MAX_INT32
)
565 , mbFormatting(false)
571 , mbThousandSep(true)
576 NumericFormatter::~NumericFormatter()
580 void NumericFormatter::SetMin( sal_Int64 nNewMin
)
583 if ( !IsEmptyFieldValue() )
587 void NumericFormatter::SetMax( sal_Int64 nNewMax
)
590 if ( !IsEmptyFieldValue() )
594 void NumericFormatter::SetUseThousandSep( bool bValue
)
596 mbThousandSep
= bValue
;
600 void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits
)
602 mnDecimalDigits
= nDigits
;
606 void NumericFormatter::SetValue( sal_Int64 nNewValue
)
608 SetUserValue( nNewValue
);
609 SetEmptyFieldValueData( false );
612 OUString
NumericFormatter::CreateFieldText( sal_Int64 nValue
) const
614 return ImplGetLocaleDataWrapper().getNum( nValue
, GetDecimalDigits(), IsUseThousandSep(), /*ShowTrailingZeros*/true );
617 void NumericFormatter::ImplSetUserValue( sal_Int64 nNewValue
, Selection
const * pNewSelection
)
619 nNewValue
= ClipAgainstMinMax(nNewValue
);
620 mnLastValue
= nNewValue
;
623 FormatValue(pNewSelection
);
626 void NumericFormatter::SetUserValue( sal_Int64 nNewValue
)
628 ImplSetUserValue( nNewValue
);
631 sal_Int64
NumericFormatter::GetValueFromString(const OUString
& rStr
) const
633 sal_Int64 nTempValue
;
635 if (ImplNumericGetValue(rStr
, nTempValue
,
636 GetDecimalDigits(), ImplGetLocaleDataWrapper()))
638 return ClipAgainstMinMax(nTempValue
);
644 OUString
NumericFormatter::GetValueString() const
646 return Application::GetSettings().GetNeutralLocaleDataWrapper().
647 getNum(GetValue(), GetDecimalDigits(), false, false);
650 // currently used by online
651 void NumericFormatter::SetValueFromString(const OUString
& rStr
)
655 if (ImplNumericGetValue(rStr
, nValue
, GetDecimalDigits(),
656 Application::GetSettings().GetNeutralLocaleDataWrapper()))
658 ImplNewFieldValue(nValue
);
662 SAL_WARN("vcl", "fail to convert the value: " << rStr
);
666 sal_Int64
NumericFormatter::GetValue() const
668 if (mbFormatting
) //don't parse the entry if we're currently formatting what to put in it
671 return GetField() ? GetValueFromString(GetField()->GetText()) : 0;
674 sal_Int64
NumericFormatter::Normalize( sal_Int64 nValue
) const
676 return (nValue
* ImplPower10( GetDecimalDigits() ) );
679 sal_Int64
NumericFormatter::Denormalize( sal_Int64 nValue
) const
681 sal_Int64 nFactor
= ImplPower10( GetDecimalDigits() );
683 if ((nValue
< ( SAL_MIN_INT64
+ nFactor
)) ||
684 (nValue
> ( SAL_MAX_INT64
- nFactor
)))
686 return ( nValue
/ nFactor
);
691 sal_Int64 nHalf
= nFactor
/ 2;
692 return ((nValue
- nHalf
) / nFactor
);
696 sal_Int64 nHalf
= nFactor
/ 2;
697 return ((nValue
+ nHalf
) / nFactor
);
701 void NumericFormatter::Reformat()
706 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
709 ImplNumericReformat();
712 void NumericFormatter::FieldUp()
714 sal_Int64 nValue
= GetValue();
715 sal_Int64 nRemainder
= nValue
% mnSpinSize
;
717 nValue
= (nRemainder
== 0) ? nValue
+ mnSpinSize
: nValue
+ mnSpinSize
- nRemainder
;
719 nValue
= (nRemainder
== 0) ? nValue
+ mnSpinSize
: nValue
- nRemainder
;
721 nValue
= ClipAgainstMinMax(nValue
);
723 ImplNewFieldValue( nValue
);
726 void NumericFormatter::FieldDown()
728 sal_Int64 nValue
= GetValue();
729 sal_Int64 nRemainder
= nValue
% mnSpinSize
;
731 nValue
= (nRemainder
== 0) ? nValue
- mnSpinSize
: nValue
- nRemainder
;
733 nValue
= (nRemainder
== 0) ? nValue
- mnSpinSize
: nValue
- mnSpinSize
- nRemainder
;
735 nValue
= ClipAgainstMinMax(nValue
);
737 ImplNewFieldValue( nValue
);
740 void NumericFormatter::FieldFirst()
742 ImplNewFieldValue( mnFirst
);
745 void NumericFormatter::FieldLast()
747 ImplNewFieldValue( mnLast
);
750 void NumericFormatter::ImplNewFieldValue( sal_Int64 nNewValue
)
755 // !!! We should check why we do not validate in ImplSetUserValue() if the value was
756 // changed. This should be done there as well since otherwise the call to Modify would not
757 // be allowed. Anyway, the paths from ImplNewFieldValue, ImplSetUserValue, and ImplSetText
758 // should be checked and clearly traced (with comment) in order to find out what happens.
760 Selection aSelection
= GetField()->GetSelection();
761 aSelection
.Normalize();
762 OUString aText
= GetField()->GetText();
763 // leave it as is if selected until end
764 if ( static_cast<sal_Int32
>(aSelection
.Max()) == aText
.getLength() )
766 if ( !aSelection
.Len() )
767 aSelection
.Min() = SELECTION_MAX
;
768 aSelection
.Max() = SELECTION_MAX
;
771 sal_Int64 nOldLastValue
= mnLastValue
;
772 ImplSetUserValue( nNewValue
, &aSelection
);
773 mnLastValue
= nOldLastValue
;
775 // Modify during Edit is only set during KeyInput
776 if ( GetField()->GetText() != aText
)
778 GetField()->SetModifyFlag();
779 GetField()->Modify();
783 sal_Int64
NumericFormatter::ClipAgainstMinMax(sal_Int64 nValue
) const
787 else if (nValue
< mnMin
)
794 Size
calcMinimumSize(const Edit
&rSpinField
, const NumericFormatter
&rFormatter
)
799 nTextLen
= std::u16string_view(OUString::number(rFormatter
.GetMin())).size();
800 string::padToLength(aBuf
, nTextLen
, '9');
801 Size aMinTextSize
= rSpinField
.CalcMinimumSizeForText(
802 rFormatter
.CreateFieldText(OUString::unacquired(aBuf
).toInt64()));
805 nTextLen
= std::u16string_view(OUString::number(rFormatter
.GetMax())).size();
806 string::padToLength(aBuf
, nTextLen
, '9');
807 Size aMaxTextSize
= rSpinField
.CalcMinimumSizeForText(
808 rFormatter
.CreateFieldText(OUString::unacquired(aBuf
).toInt64()));
811 Size
aRet(std::max(aMinTextSize
.Width(), aMaxTextSize
.Width()),
812 std::max(aMinTextSize
.Height(), aMaxTextSize
.Height()));
814 OUStringBuffer
sBuf("999999999");
815 sal_uInt16 nDigits
= rFormatter
.GetDecimalDigits();
819 string::padToLength(aBuf
, aBuf
.getLength() + nDigits
, '9');
821 aMaxTextSize
= rSpinField
.CalcMinimumSizeForText(sBuf
.makeStringAndClear());
822 aRet
.setWidth( std::min(aRet
.Width(), aMaxTextSize
.Width()) );
828 NumericBox::NumericBox(vcl::Window
* pParent
, WinBits nWinStyle
)
829 : ComboBox(pParent
, nWinStyle
)
830 , NumericFormatter(this)
833 if ( !(nWinStyle
& WB_HIDE
) )
837 void NumericBox::dispose()
843 Size
NumericBox::CalcMinimumSize() const
845 Size
aRet(calcMinimumSize(*this, *this));
849 Size
aComboSugg(ComboBox::CalcMinimumSize());
850 aRet
.setWidth( std::max(aRet
.Width(), aComboSugg
.Width()) );
851 aRet
.setHeight( std::max(aRet
.Height(), aComboSugg
.Height()) );
857 bool NumericBox::PreNotify( NotifyEvent
& rNEvt
)
859 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
861 if ( ImplNumericProcessKeyInput( *rNEvt
.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
865 return ComboBox::PreNotify( rNEvt
);
868 bool NumericBox::EventNotify( NotifyEvent
& rNEvt
)
870 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
871 MarkToBeReformatted( false );
872 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
874 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
878 return ComboBox::EventNotify( rNEvt
);
881 void NumericBox::DataChanged( const DataChangedEvent
& rDCEvt
)
883 ComboBox::DataChanged( rDCEvt
);
885 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
887 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
888 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
889 ImplResetLocaleDataWrapper();
890 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
891 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
892 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
897 void NumericBox::Modify()
899 MarkToBeReformatted( true );
903 void NumericBox::ImplNumericReformat( const OUString
& rStr
, sal_Int64
& rValue
,
906 if (ImplNumericGetValue(rStr
, rValue
, GetDecimalDigits(), ImplGetLocaleDataWrapper()))
908 sal_Int64 nTempVal
= ClipAgainstMinMax(rValue
);
909 rOutStr
= CreateFieldText( nTempVal
);
913 void NumericBox::ReformatAll()
917 SetUpdateMode( false );
918 sal_Int32 nEntryCount
= GetEntryCount();
919 for ( sal_Int32 i
=0; i
< nEntryCount
; i
++ )
921 ImplNumericReformat( GetEntry( i
), nValue
, aStr
);
923 InsertEntry( aStr
, i
);
925 NumericFormatter::Reformat();
926 SetUpdateMode( true );
929 static bool ImplMetricProcessKeyInput( const KeyEvent
& rKEvt
,
930 bool bUseThousandSep
, const LocaleDataWrapper
& rWrapper
)
932 // no meaningful strict format; therefore allow all characters
933 return ImplNumericProcessKeyInput( rKEvt
, false, bUseThousandSep
, rWrapper
);
936 static OUString
ImplMetricGetUnitText(std::u16string_view rStr
)
940 for (sal_Int32 i
= static_cast<sal_Int32
>(rStr
.size())-1; i
>= 0; --i
)
942 sal_Unicode c
= rStr
[i
];
943 if ( (c
== '\'') || (c
== '\"') || (c
== '%') || (c
== 0x2032) || (c
== 0x2033) || unicode::isAlpha(c
) || unicode::isControl(c
) )
951 return aStr
.makeStringAndClear();
954 // #104355# support localized measurements
956 static OUString
ImplMetricToString( FieldUnit rUnit
)
958 // return unit's default string (ie, the first one )
959 for (auto const& elem
: ImplGetFieldUnits())
961 if (elem
.second
== rUnit
)
970 FieldUnit
StringToMetric(const OUString
&rMetricString
)
973 OUString aStr
= rMetricString
.toAsciiLowerCase().replaceAll(" ", "");
974 for (auto const& elem
: ImplGetCleanedFieldUnits())
976 if ( elem
.first
== aStr
)
980 return FieldUnit::NONE
;
984 static FieldUnit
ImplMetricGetUnit(std::u16string_view rStr
)
986 OUString aStr
= ImplMetricGetUnitText(rStr
);
987 return StringToMetric(aStr
);
990 static FieldUnit
ImplMap2FieldUnit( MapUnit meUnit
, tools::Long
& nDecDigits
)
994 case MapUnit::Map100thMM
:
996 return FieldUnit::MM
;
997 case MapUnit::Map10thMM
:
999 return FieldUnit::MM
;
1000 case MapUnit::MapMM
:
1001 return FieldUnit::MM
;
1002 case MapUnit::MapCM
:
1003 return FieldUnit::CM
;
1004 case MapUnit::Map1000thInch
:
1006 return FieldUnit::INCH
;
1007 case MapUnit::Map100thInch
:
1009 return FieldUnit::INCH
;
1010 case MapUnit::Map10thInch
:
1012 return FieldUnit::INCH
;
1013 case MapUnit::MapInch
:
1014 return FieldUnit::INCH
;
1015 case MapUnit::MapPoint
:
1016 return FieldUnit::POINT
;
1017 case MapUnit::MapTwip
:
1018 return FieldUnit::TWIP
;
1020 OSL_FAIL( "default eInUnit" );
1023 return FieldUnit::NONE
;
1026 static double nonValueDoubleToValueDouble( double nValue
)
1028 return std::isfinite( nValue
) ? nValue
: 0.0;
1033 sal_Int64
ConvertValue(sal_Int64 nValue
, sal_Int64 mnBaseValue
, sal_uInt16 nDecDigits
,
1034 FieldUnit eInUnit
, FieldUnit eOutUnit
)
1036 double nDouble
= nonValueDoubleToValueDouble(vcl::ConvertDoubleValue(
1037 static_cast<double>(nValue
), mnBaseValue
, nDecDigits
, eInUnit
, eOutUnit
));
1040 // caution: precision loss in double cast
1041 if ( nDouble
<= double(SAL_MIN_INT64
) )
1042 nLong
= SAL_MIN_INT64
;
1043 else if ( nDouble
>= double(SAL_MAX_INT64
) )
1044 nLong
= SAL_MAX_INT64
;
1046 nLong
= static_cast<sal_Int64
>( std::round(nDouble
) );
1054 bool checkConversionUnits(MapUnit eInUnit
, FieldUnit eOutUnit
)
1056 return eOutUnit
!= FieldUnit::PERCENT
1057 && eOutUnit
!= FieldUnit::CUSTOM
1058 && eOutUnit
!= FieldUnit::NONE
1059 && eInUnit
!= MapUnit::MapPixel
1060 && eInUnit
!= MapUnit::MapSysFont
1061 && eInUnit
!= MapUnit::MapAppFont
1062 && eInUnit
!= MapUnit::MapRelative
;
1065 double convertValue( double nValue
, tools::Long nDigits
, FieldUnit eInUnit
, FieldUnit eOutUnit
)
1078 nValue
*= ImplPower10(nDigits
);
1081 if ( eInUnit
!= eOutUnit
)
1083 const o3tl::Length eFrom
= FieldToO3tlLength(eInUnit
), eTo
= FieldToO3tlLength(eOutUnit
);
1084 if (eFrom
!= o3tl::Length::invalid
&& eTo
!= o3tl::Length::invalid
)
1085 nValue
= o3tl::convert(nValue
, eFrom
, eTo
);
1095 sal_Int64
ConvertValue( sal_Int64 nValue
, sal_uInt16 nDigits
,
1096 MapUnit eInUnit
, FieldUnit eOutUnit
)
1098 if ( !checkConversionUnits(eInUnit
, eOutUnit
) )
1100 OSL_FAIL( "invalid parameters" );
1104 tools::Long nDecDigits
= nDigits
;
1105 FieldUnit eFieldUnit
= ImplMap2FieldUnit( eInUnit
, nDecDigits
);
1107 // Avoid sal_Int64 <-> double conversion issues if possible:
1108 if (eFieldUnit
== eOutUnit
&& nDigits
== 0)
1113 return static_cast<sal_Int64
>(
1114 nonValueDoubleToValueDouble(
1115 convertValue( nValue
, nDecDigits
, eFieldUnit
, eOutUnit
) ) );
1118 double ConvertDoubleValue(double nValue
, sal_Int64 mnBaseValue
, sal_uInt16 nDecDigits
,
1119 FieldUnit eInUnit
, FieldUnit eOutUnit
)
1121 if ( eInUnit
!= eOutUnit
)
1123 if (eInUnit
== FieldUnit::PERCENT
&& mnBaseValue
> 0 && nValue
> 0)
1125 sal_Int64 nDiv
= 100 * ImplPower10(nDecDigits
);
1127 if (mnBaseValue
!= 1)
1128 nValue
*= mnBaseValue
;
1135 const o3tl::Length eFrom
= FieldToO3tlLength(eInUnit
, o3tl::Length::invalid
);
1136 const o3tl::Length eTo
= FieldToO3tlLength(eOutUnit
, o3tl::Length::invalid
);
1137 if (eFrom
!= o3tl::Length::invalid
&& eTo
!= o3tl::Length::invalid
)
1138 nValue
= o3tl::convert(nValue
, eFrom
, eTo
);
1145 double ConvertDoubleValue(double nValue
, sal_uInt16 nDigits
,
1146 MapUnit eInUnit
, FieldUnit eOutUnit
)
1148 if ( !checkConversionUnits(eInUnit
, eOutUnit
) )
1150 OSL_FAIL( "invalid parameters" );
1154 tools::Long nDecDigits
= nDigits
;
1155 FieldUnit eFieldUnit
= ImplMap2FieldUnit( eInUnit
, nDecDigits
);
1157 return convertValue(nValue
, nDecDigits
, eFieldUnit
, eOutUnit
);
1160 double ConvertDoubleValue(double nValue
, sal_uInt16 nDigits
,
1161 FieldUnit eInUnit
, MapUnit eOutUnit
)
1163 if ( eInUnit
== FieldUnit::PERCENT
||
1164 eInUnit
== FieldUnit::CUSTOM
||
1165 eInUnit
== FieldUnit::NONE
||
1166 eInUnit
== FieldUnit::DEGREE
||
1167 eInUnit
== FieldUnit::SECOND
||
1168 eInUnit
== FieldUnit::MILLISECOND
||
1169 eInUnit
== FieldUnit::PIXEL
||
1170 eInUnit
== FieldUnit::FONT_EM
||
1171 eInUnit
== FieldUnit::FONT_CJK_ADVANCE
||
1172 eOutUnit
== MapUnit::MapPixel
||
1173 eOutUnit
== MapUnit::MapSysFont
||
1174 eOutUnit
== MapUnit::MapAppFont
||
1175 eOutUnit
== MapUnit::MapRelative
)
1177 OSL_FAIL( "invalid parameters" );
1181 tools::Long nDecDigits
= nDigits
;
1182 FieldUnit eFieldUnit
= ImplMap2FieldUnit( eOutUnit
, nDecDigits
);
1184 if ( nDecDigits
< 0 )
1186 nValue
*= ImplPower10(-nDecDigits
);
1190 nValue
/= ImplPower10(nDecDigits
);
1193 if ( eFieldUnit
!= eInUnit
)
1195 const o3tl::Length eFrom
= FieldToO3tlLength(eInUnit
, o3tl::Length::invalid
);
1196 const o3tl::Length eTo
= FieldToO3tlLength(eFieldUnit
, o3tl::Length::invalid
);
1197 if (eFrom
!= o3tl::Length::invalid
&& eTo
!= o3tl::Length::invalid
)
1198 nValue
= o3tl::convert(nValue
, eFrom
, eTo
);
1206 bool TextToValue(const OUString
& rStr
, double& rValue
, sal_Int64 nBaseValue
,
1207 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
, FieldUnit eUnit
)
1211 if ( !ImplNumericGetValue( rStr
, nValue
, nDecDigits
, rLocaleDataWrapper
) )
1215 FieldUnit eEntryUnit
= ImplMetricGetUnit( rStr
);
1218 // caution: conversion to double loses precision
1219 rValue
= vcl::ConvertDoubleValue(static_cast<double>(nValue
), nBaseValue
, nDecDigits
, eEntryUnit
, eUnit
);
1224 FieldUnit
GetTextMetricUnit(std::u16string_view aStr
) { return ImplMetricGetUnit(aStr
); }
1227 void MetricFormatter::ImplMetricReformat( const OUString
& rStr
, double& rValue
, OUString
& rOutStr
)
1229 if (!vcl::TextToValue(rStr
, rValue
, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit
))
1232 double nTempVal
= rValue
;
1233 // caution: precision loss in double cast
1234 if ( nTempVal
> GetMax() )
1235 nTempVal
= static_cast<double>(GetMax());
1236 else if ( nTempVal
< GetMin())
1237 nTempVal
= static_cast<double>(GetMin());
1238 rOutStr
= CreateFieldText( static_cast<sal_Int64
>(nTempVal
) );
1241 MetricFormatter::MetricFormatter(Edit
* pEdit
)
1242 : NumericFormatter(pEdit
)
1243 , meUnit(FieldUnit::NONE
)
1247 MetricFormatter::~MetricFormatter()
1251 void MetricFormatter::SetUnit( FieldUnit eNewUnit
)
1253 if (eNewUnit
== FieldUnit::MM_100TH
)
1255 SetDecimalDigits( GetDecimalDigits() + 2 );
1256 meUnit
= FieldUnit::MM
;
1263 void MetricFormatter::SetCustomUnitText( const OUString
& rStr
)
1265 maCustomUnitText
= rStr
;
1269 void MetricFormatter::SetValue( sal_Int64 nNewValue
, FieldUnit eInUnit
)
1271 SetUserValue( nNewValue
, eInUnit
);
1274 OUString
MetricFormatter::CreateFieldText( sal_Int64 nValue
) const
1276 //whether percent is separated from its number is locale
1277 //specific, pawn it off to icu to decide
1278 if (meUnit
== FieldUnit::PERCENT
)
1280 double dValue
= nValue
;
1281 dValue
/= ImplPower10(GetDecimalDigits());
1282 return unicode::formatPercent(dValue
, GetLanguageTag());
1285 OUString aStr
= NumericFormatter::CreateFieldText( nValue
);
1287 if( meUnit
== FieldUnit::CUSTOM
)
1288 aStr
+= maCustomUnitText
;
1291 OUString aSuffix
= ImplMetricToString( meUnit
);
1292 if (meUnit
!= FieldUnit::NONE
&& meUnit
!= FieldUnit::DEGREE
&& meUnit
!= FieldUnit::INCH
&& meUnit
!= FieldUnit::FOOT
)
1294 if (meUnit
== FieldUnit::INCH
)
1296 OUString sDoublePrime
= u
"\u2033"_ustr
;
1297 if (aSuffix
!= "\"" && aSuffix
!= sDoublePrime
)
1300 aSuffix
= sDoublePrime
;
1302 else if (meUnit
== FieldUnit::FOOT
)
1304 OUString sPrime
= u
"\u2032"_ustr
;
1305 if (aSuffix
!= "'" && aSuffix
!= sPrime
)
1311 assert(meUnit
!= FieldUnit::PERCENT
);
1317 void MetricFormatter::SetUserValue( sal_Int64 nNewValue
, FieldUnit eInUnit
)
1319 // convert to previously configured units
1320 nNewValue
= vcl::ConvertValue( nNewValue
, 0, GetDecimalDigits(), eInUnit
, meUnit
);
1321 NumericFormatter::SetUserValue( nNewValue
);
1324 sal_Int64
MetricFormatter::GetValueFromStringUnit(const OUString
& rStr
, FieldUnit eOutUnit
) const
1327 // caution: precision loss in double cast
1328 if (!vcl::TextToValue(rStr
, nTempValue
, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit
))
1329 nTempValue
= static_cast<double>(mnLastValue
);
1331 // caution: precision loss in double cast
1332 if (nTempValue
> mnMax
)
1333 nTempValue
= static_cast<double>(mnMax
);
1334 else if (nTempValue
< mnMin
)
1335 nTempValue
= static_cast<double>(mnMin
);
1337 // convert to requested units
1338 return vcl::ConvertValue(static_cast<sal_Int64
>(nTempValue
), 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1341 sal_Int64
MetricFormatter::GetValueFromString(const OUString
& rStr
) const
1343 return GetValueFromStringUnit(rStr
, FieldUnit::NONE
);
1346 sal_Int64
MetricFormatter::GetValue( FieldUnit eOutUnit
) const
1348 return GetField() ? GetValueFromStringUnit(GetField()->GetText(), eOutUnit
) : 0;
1351 void MetricFormatter::SetValue( sal_Int64 nValue
)
1353 // Implementation not inline, because it is a virtual Function
1354 SetValue( nValue
, FieldUnit::NONE
);
1357 void MetricFormatter::SetMin( sal_Int64 nNewMin
, FieldUnit eInUnit
)
1359 // convert to requested units
1360 NumericFormatter::SetMin(vcl::ConvertValue(nNewMin
, 0, GetDecimalDigits(), eInUnit
, meUnit
));
1363 sal_Int64
MetricFormatter::GetMin( FieldUnit eOutUnit
) const
1365 // convert to requested units
1366 return vcl::ConvertValue(NumericFormatter::GetMin(), 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1369 void MetricFormatter::SetMax( sal_Int64 nNewMax
, FieldUnit eInUnit
)
1371 // convert to requested units
1372 NumericFormatter::SetMax(vcl::ConvertValue(nNewMax
, 0, GetDecimalDigits(), eInUnit
, meUnit
));
1375 sal_Int64
MetricFormatter::GetMax( FieldUnit eOutUnit
) const
1377 // convert to requested units
1378 return vcl::ConvertValue(NumericFormatter::GetMax(), 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1381 void MetricFormatter::Reformat()
1386 OUString aText
= GetField()->GetText();
1389 // caution: precision loss in double cast
1390 double nTemp
= static_cast<double>(mnLastValue
);
1391 ImplMetricReformat( aText
, nTemp
, aStr
);
1392 mnLastValue
= static_cast<sal_Int64
>(nTemp
);
1394 if ( !aStr
.isEmpty() )
1396 ImplSetText( aStr
);
1399 SetValue( mnLastValue
);
1402 sal_Int64
MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit
) const
1404 // convert to requested units
1405 return vcl::ConvertValue(0/*nCorrectedValue*/, 0, GetDecimalDigits(),
1409 MetricField::MetricField(vcl::Window
* pParent
, WinBits nWinStyle
)
1410 : SpinField(pParent
, nWinStyle
, WindowType::METRICFIELD
)
1411 , MetricFormatter(this)
1416 void MetricField::dispose()
1419 SpinField::dispose();
1422 Size
MetricField::CalcMinimumSize() const
1424 return calcMinimumSize(*this, *this);
1427 bool MetricField::set_property(const OUString
&rKey
, const OUString
&rValue
)
1429 if (rKey
== "digits")
1430 SetDecimalDigits(rValue
.toInt32());
1431 else if (rKey
== "spin-size")
1432 SetSpinSize(rValue
.toInt32());
1434 return SpinField::set_property(rKey
, rValue
);
1438 void MetricField::SetUnit( FieldUnit nNewUnit
)
1440 sal_Int64 nRawMax
= GetMax( nNewUnit
);
1441 sal_Int64 nMax
= Denormalize( nRawMax
);
1442 sal_Int64 nMin
= Denormalize( GetMin( nNewUnit
) );
1443 sal_Int64 nFirst
= Denormalize( GetFirst( nNewUnit
) );
1444 sal_Int64 nLast
= Denormalize( GetLast( nNewUnit
) );
1446 MetricFormatter::SetUnit( nNewUnit
);
1448 SetMax( Normalize( nMax
), nNewUnit
);
1449 SetMin( Normalize( nMin
), nNewUnit
);
1450 SetFirst( Normalize( nFirst
), nNewUnit
);
1451 SetLast( Normalize( nLast
), nNewUnit
);
1454 void MetricField::SetFirst( sal_Int64 nNewFirst
, FieldUnit eInUnit
)
1457 nNewFirst
= vcl::ConvertValue(nNewFirst
, 0, GetDecimalDigits(), eInUnit
, meUnit
);
1458 mnFirst
= nNewFirst
;
1461 sal_Int64
MetricField::GetFirst( FieldUnit eOutUnit
) const
1464 return vcl::ConvertValue(mnFirst
, 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1467 void MetricField::SetLast( sal_Int64 nNewLast
, FieldUnit eInUnit
)
1470 nNewLast
= vcl::ConvertValue(nNewLast
, 0, GetDecimalDigits(), eInUnit
, meUnit
);
1474 sal_Int64
MetricField::GetLast( FieldUnit eOutUnit
) const
1477 return vcl::ConvertValue(mnLast
, 0, GetDecimalDigits(), meUnit
, eOutUnit
);
1480 bool MetricField::PreNotify( NotifyEvent
& rNEvt
)
1482 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
1484 if ( ImplMetricProcessKeyInput( *rNEvt
.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1488 return SpinField::PreNotify( rNEvt
);
1491 bool MetricField::EventNotify( NotifyEvent
& rNEvt
)
1493 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
1494 MarkToBeReformatted( false );
1495 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
1497 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1501 return SpinField::EventNotify( rNEvt
);
1504 void MetricField::DataChanged( const DataChangedEvent
& rDCEvt
)
1506 SpinField::DataChanged( rDCEvt
);
1508 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
1510 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1511 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1512 ImplResetLocaleDataWrapper();
1513 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1514 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1515 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
1520 void MetricField::Modify()
1522 MarkToBeReformatted( true );
1523 SpinField::Modify();
1526 void MetricField::Up()
1532 void MetricField::Down()
1538 void MetricField::First()
1544 void MetricField::Last()
1550 void MetricField::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
)
1552 SpinField::DumpAsPropertyTree(rJsonWriter
);
1553 rJsonWriter
.put("min", GetMin());
1554 rJsonWriter
.put("max", GetMax());
1555 rJsonWriter
.put("unit", FieldUnitToString(GetUnit()));
1556 OUString sValue
= Application::GetSettings().GetNeutralLocaleDataWrapper().
1557 getNum(GetValue(), GetDecimalDigits(), false, false);
1558 rJsonWriter
.put("value", sValue
);
1561 FactoryFunction
MetricField::GetUITestFactory() const
1563 return MetricFieldUIObject::create
;
1566 MetricBox::MetricBox(vcl::Window
* pParent
, WinBits nWinStyle
)
1567 : ComboBox(pParent
, nWinStyle
)
1568 , MetricFormatter(this)
1573 void MetricBox::dispose()
1576 ComboBox::dispose();
1579 Size
MetricBox::CalcMinimumSize() const
1581 Size
aRet(calcMinimumSize(*this, *this));
1583 if (IsDropDownBox())
1585 Size
aComboSugg(ComboBox::CalcMinimumSize());
1586 aRet
.setWidth( std::max(aRet
.Width(), aComboSugg
.Width()) );
1587 aRet
.setHeight( std::max(aRet
.Height(), aComboSugg
.Height()) );
1593 bool MetricBox::PreNotify( NotifyEvent
& rNEvt
)
1595 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
1597 if ( ImplMetricProcessKeyInput( *rNEvt
.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1601 return ComboBox::PreNotify( rNEvt
);
1604 bool MetricBox::EventNotify( NotifyEvent
& rNEvt
)
1606 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
1607 MarkToBeReformatted( false );
1608 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
1610 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1614 return ComboBox::EventNotify( rNEvt
);
1617 void MetricBox::DataChanged( const DataChangedEvent
& rDCEvt
)
1619 ComboBox::DataChanged( rDCEvt
);
1621 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
1623 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1624 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1625 ImplResetLocaleDataWrapper();
1626 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1627 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1628 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
1633 void MetricBox::Modify()
1635 MarkToBeReformatted( true );
1639 void MetricBox::ReformatAll()
1643 SetUpdateMode( false );
1644 sal_Int32 nEntryCount
= GetEntryCount();
1645 for ( sal_Int32 i
=0; i
< nEntryCount
; i
++ )
1647 ImplMetricReformat( GetEntry( i
), nValue
, aStr
);
1649 InsertEntry( aStr
, i
);
1651 MetricFormatter::Reformat();
1652 SetUpdateMode( true );
1655 static bool ImplCurrencyProcessKeyInput( const KeyEvent
& rKEvt
,
1656 bool bUseThousandSep
, const LocaleDataWrapper
& rWrapper
)
1658 // no strict format set; therefore allow all characters
1659 return ImplNumericProcessKeyInput( rKEvt
, false, bUseThousandSep
, rWrapper
);
1662 static bool ImplCurrencyGetValue( const OUString
& rStr
, sal_Int64
& rValue
,
1663 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rWrapper
)
1666 return ImplNumericGetValue( rStr
, rValue
, nDecDigits
, rWrapper
, true );
1669 void CurrencyFormatter::ImplCurrencyReformat( const OUString
& rStr
, OUString
& rOutStr
)
1672 if ( !ImplNumericGetValue( rStr
, nValue
, GetDecimalDigits(), ImplGetLocaleDataWrapper(), true ) )
1675 sal_Int64 nTempVal
= nValue
;
1676 if ( nTempVal
> GetMax() )
1677 nTempVal
= GetMax();
1678 else if ( nTempVal
< GetMin())
1679 nTempVal
= GetMin();
1680 rOutStr
= CreateFieldText( nTempVal
);
1683 CurrencyFormatter::CurrencyFormatter(Edit
* pField
)
1684 : NumericFormatter(pField
)
1688 CurrencyFormatter::~CurrencyFormatter()
1692 void CurrencyFormatter::SetValue( sal_Int64 nNewValue
)
1694 SetUserValue( nNewValue
);
1695 SetEmptyFieldValueData( false );
1698 OUString
CurrencyFormatter::CreateFieldText( sal_Int64 nValue
) const
1700 return ImplGetLocaleDataWrapper().getCurr( nValue
, GetDecimalDigits(),
1701 ImplGetLocaleDataWrapper().getCurrSymbol(),
1702 IsUseThousandSep() );
1705 sal_Int64
CurrencyFormatter::GetValueFromString(const OUString
& rStr
) const
1707 sal_Int64 nTempValue
;
1708 if ( ImplCurrencyGetValue( rStr
, nTempValue
, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
1710 return ClipAgainstMinMax(nTempValue
);
1716 void CurrencyFormatter::Reformat()
1722 ImplCurrencyReformat( GetField()->GetText(), aStr
);
1724 if ( !aStr
.isEmpty() )
1726 ImplSetText( aStr
);
1727 sal_Int64 nTemp
= mnLastValue
;
1728 ImplCurrencyGetValue( aStr
, nTemp
, GetDecimalDigits(), ImplGetLocaleDataWrapper() );
1729 mnLastValue
= nTemp
;
1732 SetValue( mnLastValue
);
1735 CurrencyField::CurrencyField(vcl::Window
* pParent
, WinBits nWinStyle
)
1736 : SpinField(pParent
, nWinStyle
)
1737 , CurrencyFormatter(this)
1742 void CurrencyField::dispose()
1745 SpinField::dispose();
1748 bool CurrencyField::PreNotify( NotifyEvent
& rNEvt
)
1750 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
1752 if ( ImplCurrencyProcessKeyInput( *rNEvt
.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1756 return SpinField::PreNotify( rNEvt
);
1759 bool CurrencyField::EventNotify( NotifyEvent
& rNEvt
)
1761 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
1762 MarkToBeReformatted( false );
1763 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
1765 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1769 return SpinField::EventNotify( rNEvt
);
1772 void CurrencyField::DataChanged( const DataChangedEvent
& rDCEvt
)
1774 SpinField::DataChanged( rDCEvt
);
1776 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
1778 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1779 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1780 ImplResetLocaleDataWrapper();
1781 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1782 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1783 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
1788 void CurrencyField::Modify()
1790 MarkToBeReformatted( true );
1791 SpinField::Modify();
1794 void CurrencyField::Up()
1800 void CurrencyField::Down()
1806 void CurrencyField::First()
1812 void CurrencyField::Last()
1818 CurrencyBox::CurrencyBox(vcl::Window
* pParent
, WinBits nWinStyle
)
1819 : ComboBox(pParent
, nWinStyle
)
1820 , CurrencyFormatter(this)
1825 void CurrencyBox::dispose()
1828 ComboBox::dispose();
1831 bool CurrencyBox::PreNotify( NotifyEvent
& rNEvt
)
1833 if ( (rNEvt
.GetType() == NotifyEventType::KEYINPUT
) && !rNEvt
.GetKeyEvent()->GetKeyCode().IsMod2() )
1835 if ( ImplCurrencyProcessKeyInput( *rNEvt
.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1839 return ComboBox::PreNotify( rNEvt
);
1842 bool CurrencyBox::EventNotify( NotifyEvent
& rNEvt
)
1844 if ( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
1845 MarkToBeReformatted( false );
1846 else if ( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
1848 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1852 return ComboBox::EventNotify( rNEvt
);
1855 void CurrencyBox::DataChanged( const DataChangedEvent
& rDCEvt
)
1857 ComboBox::DataChanged( rDCEvt
);
1859 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) && (rDCEvt
.GetFlags() & AllSettingsFlags::LOCALE
) )
1861 OUString sOldDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1862 OUString sOldThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1863 ImplResetLocaleDataWrapper();
1864 OUString sNewDecSep
= ImplGetLocaleDataWrapper().getNumDecimalSep();
1865 OUString sNewThSep
= ImplGetLocaleDataWrapper().getNumThousandSep();
1866 ImplUpdateSeparators( sOldDecSep
, sNewDecSep
, sOldThSep
, sNewThSep
, this );
1871 void CurrencyBox::Modify()
1873 MarkToBeReformatted( true );
1877 void CurrencyBox::ReformatAll()
1880 SetUpdateMode( false );
1881 sal_Int32 nEntryCount
= GetEntryCount();
1882 for ( sal_Int32 i
=0; i
< nEntryCount
; i
++ )
1884 ImplCurrencyReformat( GetEntry( i
), aStr
);
1886 InsertEntry( aStr
, i
);
1888 CurrencyFormatter::Reformat();
1889 SetUpdateMode( true );
1892 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */