Simplify using designated initializers
[LibreOffice.git] / vcl / source / control / field.cxx
blobd0ae78e907bf1188b7d38e89bab2dd4ce3e2f219
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <cmath>
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>
41 #include <svdata.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;
54 namespace
57 std::string FieldUnitToString(FieldUnit unit)
59 switch(unit)
61 case FieldUnit::NONE:
62 return "";
64 case FieldUnit::MM:
65 return "mm";
67 case FieldUnit::CM:
68 return "cm";
70 case FieldUnit::M:
71 return "m";
73 case FieldUnit::KM:
74 return "km";
76 case FieldUnit::TWIP:
77 return "twip";
79 case FieldUnit::POINT:
80 return "point";
82 case FieldUnit::PICA:
83 return "pica";
85 case FieldUnit::INCH:
86 return "inch";
88 case FieldUnit::FOOT:
89 return "foot";
91 case FieldUnit::MILE:
92 return "mile";
94 case FieldUnit::CHAR:
95 return "char";
97 case FieldUnit::LINE:
98 return "line";
100 case FieldUnit::CUSTOM:
101 return "custom";
103 case FieldUnit::PERCENT:
104 return "percent";
106 case FieldUnit::MM_100TH:
107 return "mm100th";
109 case FieldUnit::PIXEL:
110 return "pixel";
112 case FieldUnit::DEGREE:
113 return "degree";
115 case FieldUnit::SECOND:
116 return "second";
118 case FieldUnit::MILLISECOND:
119 return "millisecond";
121 case FieldUnit::FONT_EM:
122 return "em";
124 case FieldUnit::FONT_CJK_ADVANCE:
125 return "ic";
128 return "";
131 sal_Int64 ImplPower10( sal_uInt16 n )
133 sal_uInt16 i;
134 sal_Int64 nValue = 1;
136 for ( i=0; i < n; i++ )
137 nValue *= 10;
139 return nValue;
142 bool ImplNumericProcessKeyInput( const KeyEvent& rKEvt,
143 bool bStrictFormat, bool bThousandSep,
144 const LocaleDataWrapper& rLocaleDataWrapper )
146 if ( !bStrictFormat )
147 return false;
148 else
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) ||
160 (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;
171 bool bFrac = false;
172 sal_Int32 nDecPos, nFracDivPos;
173 sal_Int64 nValue;
175 // react on empty string
176 if ( rStr.isEmpty() )
177 return false;
179 // remove leading and trailing spaces
180 aStr = aStr.trim();
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
191 if (nFracDivPos > 0)
193 bFrac = true;
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
204 else
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));
217 else
218 aStr1 = aStr;
220 // negative?
221 if ( bCurrency )
223 if ( aStr.startsWith("(") && aStr.endsWith(")") )
224 bNegative = true;
225 if ( !bNegative )
227 for (sal_Int32 i=0; i < aStr.getLength(); i++ )
229 if ( (aStr[i] >= '0') && (aStr[i] <= '9') )
230 break;
231 else if ( aStr[i] == '-' )
233 bNegative = true;
234 break;
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') )
247 break;
248 else if ( aStr[i] == '-' )
250 bNegative = true;
251 break;
257 else
259 if ( !aStr1.isEmpty() && aStr1[0] == '-')
260 bNegative = true;
261 if ( !aStrNum.isEmpty() && aStrNum[0] == '-') // For non-mixed fractions
262 bNegative = true;
265 // remove all unwanted characters
266 // For whole number
267 for (sal_Int32 i=0; i < aStr1.getLength(); )
269 if ( (aStr1[i] >= '0') && (aStr1[i] <= '9') )
270 i++;
271 else
272 aStr1.remove( i, 1 );
274 // For decimal
275 if (!bFrac) {
276 for (sal_Int32 i=0; i < aStr2.getLength(); )
278 if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
279 ++i;
280 else
281 aStr2.remove(i, 1);
284 else {
285 // for numerator
286 for (sal_Int32 i=0; i < aStrNum.getLength(); )
288 if ((aStrNum[i] >= '0') && (aStrNum[i] <= '9'))
289 ++i;
290 else
291 aStrNum.remove(i, 1);
293 // for denominator
294 for (sal_Int32 i=0; i < aStrDenom.getLength(); )
296 if ((aStrDenom[i] >= '0') && (aStrDenom[i] <= '9'))
297 ++i;
298 else
299 aStrDenom.remove(i, 1);
304 if ( !bFrac && aStr1.isEmpty() && aStr2.isEmpty() )
305 return false;
306 else if ( bFrac && aStr1.isEmpty() && (aStrNum.isEmpty() || aStrDenom.isEmpty()) )
307 return false;
309 if ( aStr1.isEmpty() )
310 aStr1 = "0";
311 if ( bNegative )
312 aStr1.insert(0, "-");
314 // Convert fractional strings
315 if (bFrac) {
316 // Convert to fraction
317 sal_Int64 nWholeNum = o3tl::toInt64(aStr1);
318 aStr1.setLength(0);
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('.');
326 if ( nDecPos >= 0)
328 aStr1.append(aStrFrac.getStr(), nDecPos);
329 aStr2.append(aStrFrac.getStr()+nDecPos+1);
331 else
332 aStr1 = std::move(aStrFrac);
335 // prune and round fraction
336 bool bRound = false;
337 if (aStr2.getLength() > nDecDigits)
339 if (aStr2[nDecDigits] >= '5')
340 bRound = true;
341 string::truncateToLength(aStr2, nDecDigits);
343 if (aStr2.getLength() < nDecDigits)
344 string::padToLength(aStr2, nDecDigits, '0');
346 aStr = aStr1 + aStr2;
348 // check range
349 nValue = aStr.toInt64();
350 if( nValue == 0 )
352 // check if string is equivalent to zero
353 sal_Int32 nIndex = bNegative ? 1 : 0;
354 while (nIndex < aStr.getLength() && aStr[nIndex] == '0')
355 ++nIndex;
356 if( nIndex < aStr.getLength() )
358 rValue = bNegative ? SAL_MIN_INT64 : SAL_MAX_INT64;
359 return true;
362 if (bRound)
364 if ( !bNegative )
365 nValue++;
366 else
367 nValue--;
370 rValue = nValue;
372 return true;
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();
399 else
401 aBuf.append( pBuffer + nIndex );
402 nIndex = -1;
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,
411 Edit* pEdit )
413 bool bChangeDec = (rOldDecSep != rNewDecSep);
414 bool bChangeTh = (rOldThSep != rNewThSep );
416 if( !(bChangeDec || bChangeTh) )
417 return;
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);
426 if( pCombo )
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 );
440 if( bUpdateMode )
441 pEdit->SetUpdateMode( bUpdateMode );
444 } // namespace
446 FormatterBase::FormatterBase(Edit* pField)
448 mpField = pField;
449 mpLocaleDataWrapper = nullptr;
450 mbReformat = false;
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
473 // language tag
474 mpLocaleDataWrapper.reset();
477 const LocaleDataWrapper& FormatterBase::GetLocaleDataWrapper() const
479 return ImplGetLocaleDataWrapper();
482 void FormatterBase::Reformat()
486 void FormatterBase::ReformatAll()
488 Reformat();
491 void FormatterBase::SetStrictFormat( bool bStrict )
493 if ( bStrict != mbStrictFormat )
495 mbStrictFormat = bStrict;
496 if ( mbStrictFormat )
497 ReformatAll();
501 const lang::Locale& FormatterBase::GetLocale() const
503 if ( mpField )
504 return mpField->GetSettings().GetLanguageTag().getLocale();
505 else
506 return Application::GetSettings().GetLanguageTag().getLocale();
509 const LanguageTag& FormatterBase::GetLanguageTag() const
511 if ( mpField )
512 return mpField->GetSettings().GetLanguageTag();
513 else
514 return Application::GetSettings().GetLanguageTag();
517 void FormatterBase::ImplSetText( const OUString& rText, Selection const * pNewSelection )
519 if ( mpField )
521 if (pNewSelection)
522 mpField->SetText(rText, *pNewSelection);
523 else
525 Selection aSel = mpField->GetSelection();
526 aSel.Min() = aSel.Max();
527 mpField->SetText(rText, aSel);
529 MarkToBeReformatted( false );
533 void FormatterBase::SetEmptyFieldValue()
535 if ( mpField )
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)
547 mbFormatting = true;
548 ImplSetText(CreateFieldText(mnLastValue), pNewSelection);
549 mbFormatting = false;
552 void NumericFormatter::ImplNumericReformat()
554 mnLastValue = GetValue();
555 FormatValue();
558 NumericFormatter::NumericFormatter(Edit* pEdit)
559 : FormatterBase(pEdit)
560 , mnLastValue(0)
561 , mnMin(0)
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)
566 , mnSpinSize(1)
567 // for fields
568 , mnFirst(mnMin)
569 , mnLast(mnMax)
570 , mnDecimalDigits(0)
571 , mbThousandSep(true)
573 ReformatAll();
576 NumericFormatter::~NumericFormatter()
580 void NumericFormatter::SetMin( sal_Int64 nNewMin )
582 mnMin = nNewMin;
583 if ( !IsEmptyFieldValue() )
584 ReformatAll();
587 void NumericFormatter::SetMax( sal_Int64 nNewMax )
589 mnMax = nNewMax;
590 if ( !IsEmptyFieldValue() )
591 ReformatAll();
594 void NumericFormatter::SetUseThousandSep( bool bValue )
596 mbThousandSep = bValue;
597 ReformatAll();
600 void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits )
602 mnDecimalDigits = nDigits;
603 ReformatAll();
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;
622 if ( GetField() )
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);
640 else
641 return mnLastValue;
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)
653 sal_Int64 nValue;
655 if (ImplNumericGetValue(rStr, nValue, GetDecimalDigits(),
656 Application::GetSettings().GetNeutralLocaleDataWrapper()))
658 ImplNewFieldValue(nValue);
660 else
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
669 return mnLastValue;
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 );
689 if( nValue < 0 )
691 sal_Int64 nHalf = nFactor / 2;
692 return ((nValue - nHalf) / nFactor );
694 else
696 sal_Int64 nHalf = nFactor / 2;
697 return ((nValue + nHalf) / nFactor );
701 void NumericFormatter::Reformat()
703 if ( !GetField() )
704 return;
706 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
707 return;
709 ImplNumericReformat();
712 void NumericFormatter::FieldUp()
714 sal_Int64 nValue = GetValue();
715 sal_Int64 nRemainder = nValue % mnSpinSize;
716 if (nValue >= 0)
717 nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue + mnSpinSize - nRemainder;
718 else
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;
730 if (nValue >= 0)
731 nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - nRemainder;
732 else
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 )
752 if ( !GetField() )
753 return;
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
785 if (nValue > mnMax)
786 nValue = mnMax;
787 else if (nValue < mnMin)
788 nValue = mnMin;
789 return nValue;
792 namespace
794 Size calcMinimumSize(const Edit &rSpinField, const NumericFormatter &rFormatter)
796 OUStringBuffer aBuf;
797 sal_Int32 nTextLen;
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()));
803 aBuf.setLength(0);
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()));
809 aBuf.setLength(0);
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();
816 if (nDigits)
818 sBuf.append('.');
819 string::padToLength(aBuf, aBuf.getLength() + nDigits, '9');
821 aMaxTextSize = rSpinField.CalcMinimumSizeForText(sBuf.makeStringAndClear());
822 aRet.setWidth( std::min(aRet.Width(), aMaxTextSize.Width()) );
824 return aRet;
828 NumericBox::NumericBox(vcl::Window* pParent, WinBits nWinStyle)
829 : ComboBox(pParent, nWinStyle)
830 , NumericFormatter(this)
832 Reformat();
833 if ( !(nWinStyle & WB_HIDE ) )
834 Show();
837 void NumericBox::dispose()
839 ClearField();
840 ComboBox::dispose();
843 Size NumericBox::CalcMinimumSize() const
845 Size aRet(calcMinimumSize(*this, *this));
847 if (IsDropDownBox())
849 Size aComboSugg(ComboBox::CalcMinimumSize());
850 aRet.setWidth( std::max(aRet.Width(), aComboSugg.Width()) );
851 aRet.setHeight( std::max(aRet.Height(), aComboSugg.Height()) );
854 return aRet;
857 bool NumericBox::PreNotify( NotifyEvent& rNEvt )
859 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
861 if ( ImplNumericProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
862 return true;
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()) )
875 Reformat();
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 );
893 ReformatAll();
897 void NumericBox::Modify()
899 MarkToBeReformatted( true );
900 ComboBox::Modify();
903 void NumericBox::ImplNumericReformat( const OUString& rStr, sal_Int64& rValue,
904 OUString& rOutStr )
906 if (ImplNumericGetValue(rStr, rValue, GetDecimalDigits(), ImplGetLocaleDataWrapper()))
908 sal_Int64 nTempVal = ClipAgainstMinMax(rValue);
909 rOutStr = CreateFieldText( nTempVal );
913 void NumericBox::ReformatAll()
915 sal_Int64 nValue;
916 OUString aStr;
917 SetUpdateMode( false );
918 sal_Int32 nEntryCount = GetEntryCount();
919 for ( sal_Int32 i=0; i < nEntryCount; i++ )
921 ImplNumericReformat( GetEntry( i ), nValue, aStr );
922 RemoveEntryAt(i);
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)
938 // fetch unit text
939 OUStringBuffer aStr;
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) )
944 aStr.insert(0, c);
945 else
947 if (!aStr.isEmpty())
948 break;
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)
962 return elem.first;
965 return OUString();
968 namespace
970 FieldUnit StringToMetric(const OUString &rMetricString)
972 // return FieldUnit
973 OUString aStr = rMetricString.toAsciiLowerCase().replaceAll(" ", "");
974 for (auto const& elem : ImplGetCleanedFieldUnits())
976 if ( elem.first == aStr )
977 return elem.second;
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 )
992 switch( meUnit )
994 case MapUnit::Map100thMM :
995 nDecDigits -= 2;
996 return FieldUnit::MM;
997 case MapUnit::Map10thMM :
998 nDecDigits -= 1;
999 return FieldUnit::MM;
1000 case MapUnit::MapMM :
1001 return FieldUnit::MM;
1002 case MapUnit::MapCM :
1003 return FieldUnit::CM;
1004 case MapUnit::Map1000thInch :
1005 nDecDigits -= 3;
1006 return FieldUnit::INCH;
1007 case MapUnit::Map100thInch :
1008 nDecDigits -= 2;
1009 return FieldUnit::INCH;
1010 case MapUnit::Map10thInch :
1011 nDecDigits -= 1;
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;
1019 default:
1020 OSL_FAIL( "default eInUnit" );
1021 break;
1023 return FieldUnit::NONE;
1026 static double nonValueDoubleToValueDouble( double nValue )
1028 return std::isfinite( nValue ) ? nValue : 0.0;
1031 namespace vcl
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));
1038 sal_Int64 nLong ;
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;
1045 else
1046 nLong = static_cast<sal_Int64>( std::round(nDouble) );
1048 return nLong;
1052 namespace {
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 )
1067 if ( nDigits < 0 )
1069 while ( nDigits )
1071 nValue += 5;
1072 nValue /= 10;
1073 nDigits++;
1076 else
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);
1088 return nValue;
1093 namespace vcl
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" );
1101 return nValue;
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)
1110 return nValue;
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;
1130 nValue += nDiv / 2;
1131 nValue /= nDiv;
1133 else
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);
1142 return nValue;
1145 double ConvertDoubleValue(double nValue, sal_uInt16 nDigits,
1146 MapUnit eInUnit, FieldUnit eOutUnit)
1148 if ( !checkConversionUnits(eInUnit, eOutUnit) )
1150 OSL_FAIL( "invalid parameters" );
1151 return nValue;
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" );
1178 return nValue;
1181 tools::Long nDecDigits = nDigits;
1182 FieldUnit eFieldUnit = ImplMap2FieldUnit( eOutUnit, nDecDigits );
1184 if ( nDecDigits < 0 )
1186 nValue *= ImplPower10(-nDecDigits);
1188 else
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);
1200 return nValue;
1204 namespace vcl
1206 bool TextToValue(const OUString& rStr, double& rValue, sal_Int64 nBaseValue,
1207 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper, FieldUnit eUnit)
1209 // Get value
1210 sal_Int64 nValue;
1211 if ( !ImplNumericGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper ) )
1212 return false;
1214 // Determine unit
1215 FieldUnit eEntryUnit = ImplMetricGetUnit( rStr );
1217 // Recalculate unit
1218 // caution: conversion to double loses precision
1219 rValue = vcl::ConvertDoubleValue(static_cast<double>(nValue), nBaseValue, nDecDigits, eEntryUnit, eUnit);
1221 return true;
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))
1230 return;
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;
1258 else
1259 meUnit = eNewUnit;
1260 ReformatAll();
1263 void MetricFormatter::SetCustomUnitText( const OUString& rStr )
1265 maCustomUnitText = rStr;
1266 ReformatAll();
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;
1289 else
1291 OUString aSuffix = ImplMetricToString( meUnit );
1292 if (meUnit != FieldUnit::NONE && meUnit != FieldUnit::DEGREE && meUnit != FieldUnit::INCH && meUnit != FieldUnit::FOOT)
1293 aStr += " ";
1294 if (meUnit == FieldUnit::INCH)
1296 OUString sDoublePrime = u"\u2033"_ustr;
1297 if (aSuffix != "\"" && aSuffix != sDoublePrime)
1298 aStr += " ";
1299 else
1300 aSuffix = sDoublePrime;
1302 else if (meUnit == FieldUnit::FOOT)
1304 OUString sPrime = u"\u2032"_ustr;
1305 if (aSuffix != "'" && aSuffix != sPrime)
1306 aStr += " ";
1307 else
1308 aSuffix = sPrime;
1311 assert(meUnit != FieldUnit::PERCENT);
1312 aStr += aSuffix;
1314 return aStr;
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
1326 double nTempValue;
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()
1383 if ( !GetField() )
1384 return;
1386 OUString aText = GetField()->GetText();
1388 OUString aStr;
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 );
1398 else
1399 SetValue( mnLastValue );
1402 sal_Int64 MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit ) const
1404 // convert to requested units
1405 return vcl::ConvertValue(0/*nCorrectedValue*/, 0, GetDecimalDigits(),
1406 meUnit, eOutUnit);
1409 MetricField::MetricField(vcl::Window* pParent, WinBits nWinStyle)
1410 : SpinField(pParent, nWinStyle, WindowType::METRICFIELD)
1411 , MetricFormatter(this)
1413 Reformat();
1416 void MetricField::dispose()
1418 ClearField();
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());
1433 else
1434 return SpinField::set_property(rKey, rValue);
1435 return true;
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 )
1456 // convert
1457 nNewFirst = vcl::ConvertValue(nNewFirst, 0, GetDecimalDigits(), eInUnit, meUnit);
1458 mnFirst = nNewFirst;
1461 sal_Int64 MetricField::GetFirst( FieldUnit eOutUnit ) const
1463 // convert
1464 return vcl::ConvertValue(mnFirst, 0, GetDecimalDigits(), meUnit, eOutUnit);
1467 void MetricField::SetLast( sal_Int64 nNewLast, FieldUnit eInUnit )
1469 // convert
1470 nNewLast = vcl::ConvertValue(nNewLast, 0, GetDecimalDigits(), eInUnit, meUnit);
1471 mnLast = nNewLast;
1474 sal_Int64 MetricField::GetLast( FieldUnit eOutUnit ) const
1476 // convert
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() ) )
1485 return true;
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()) )
1498 Reformat();
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 );
1516 ReformatAll();
1520 void MetricField::Modify()
1522 MarkToBeReformatted( true );
1523 SpinField::Modify();
1526 void MetricField::Up()
1528 FieldUp();
1529 SpinField::Up();
1532 void MetricField::Down()
1534 FieldDown();
1535 SpinField::Down();
1538 void MetricField::First()
1540 FieldFirst();
1541 SpinField::First();
1544 void MetricField::Last()
1546 FieldLast();
1547 SpinField::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)
1570 Reformat();
1573 void MetricBox::dispose()
1575 ClearField();
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()) );
1590 return aRet;
1593 bool MetricBox::PreNotify( NotifyEvent& rNEvt )
1595 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1597 if ( ImplMetricProcessKeyInput( *rNEvt.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1598 return true;
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()) )
1611 Reformat();
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 );
1629 ReformatAll();
1633 void MetricBox::Modify()
1635 MarkToBeReformatted( true );
1636 ComboBox::Modify();
1639 void MetricBox::ReformatAll()
1641 double nValue;
1642 OUString aStr;
1643 SetUpdateMode( false );
1644 sal_Int32 nEntryCount = GetEntryCount();
1645 for ( sal_Int32 i=0; i < nEntryCount; i++ )
1647 ImplMetricReformat( GetEntry( i ), nValue, aStr );
1648 RemoveEntryAt(i);
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 )
1665 // fetch number
1666 return ImplNumericGetValue( rStr, rValue, nDecDigits, rWrapper, true );
1669 void CurrencyFormatter::ImplCurrencyReformat( const OUString& rStr, OUString& rOutStr )
1671 sal_Int64 nValue;
1672 if ( !ImplNumericGetValue( rStr, nValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), true ) )
1673 return;
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);
1712 else
1713 return mnLastValue;
1716 void CurrencyFormatter::Reformat()
1718 if ( !GetField() )
1719 return;
1721 OUString aStr;
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;
1731 else
1732 SetValue( mnLastValue );
1735 CurrencyField::CurrencyField(vcl::Window* pParent, WinBits nWinStyle)
1736 : SpinField(pParent, nWinStyle)
1737 , CurrencyFormatter(this)
1739 Reformat();
1742 void CurrencyField::dispose()
1744 ClearField();
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() ) )
1753 return true;
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()) )
1766 Reformat();
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 );
1784 ReformatAll();
1788 void CurrencyField::Modify()
1790 MarkToBeReformatted( true );
1791 SpinField::Modify();
1794 void CurrencyField::Up()
1796 FieldUp();
1797 SpinField::Up();
1800 void CurrencyField::Down()
1802 FieldDown();
1803 SpinField::Down();
1806 void CurrencyField::First()
1808 FieldFirst();
1809 SpinField::First();
1812 void CurrencyField::Last()
1814 FieldLast();
1815 SpinField::Last();
1818 CurrencyBox::CurrencyBox(vcl::Window* pParent, WinBits nWinStyle)
1819 : ComboBox(pParent, nWinStyle)
1820 , CurrencyFormatter(this)
1822 Reformat();
1825 void CurrencyBox::dispose()
1827 ClearField();
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() ) )
1836 return true;
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()) )
1849 Reformat();
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 );
1867 ReformatAll();
1871 void CurrencyBox::Modify()
1873 MarkToBeReformatted( true );
1874 ComboBox::Modify();
1877 void CurrencyBox::ReformatAll()
1879 OUString aStr;
1880 SetUpdateMode( false );
1881 sal_Int32 nEntryCount = GetEntryCount();
1882 for ( sal_Int32 i=0; i < nEntryCount; i++ )
1884 ImplCurrencyReformat( GetEntry( i ), aStr );
1885 RemoveEntryAt(i);
1886 InsertEntry( aStr, i );
1888 CurrencyFormatter::Reformat();
1889 SetUpdateMode( true );
1892 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */