bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / control / field.cxx
blob1b802660cf3a2bfbd099a4ab7661696a280c789e
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";
122 return "";
125 sal_Int64 ImplPower10( sal_uInt16 n )
127 sal_uInt16 i;
128 sal_Int64 nValue = 1;
130 for ( i=0; i < n; i++ )
131 nValue *= 10;
133 return nValue;
136 bool ImplNumericProcessKeyInput( const KeyEvent& rKEvt,
137 bool bStrictFormat, bool bThousandSep,
138 const LocaleDataWrapper& rLocaleDataWrapper )
140 if ( !bStrictFormat )
141 return false;
142 else
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) ||
154 (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;
165 bool bFrac = false;
166 sal_Int32 nDecPos, nFracDivPos;
167 sal_Int64 nValue;
169 // react on empty string
170 if ( rStr.isEmpty() )
171 return false;
173 // remove leading and trailing spaces
174 aStr = aStr.trim();
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
185 if (nFracDivPos > 0)
187 bFrac = true;
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
198 else
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));
211 else
212 aStr1 = aStr;
214 // negative?
215 if ( bCurrency )
217 if ( aStr.startsWith("(") && aStr.endsWith(")") )
218 bNegative = true;
219 if ( !bNegative )
221 for (sal_Int32 i=0; i < aStr.getLength(); i++ )
223 if ( (aStr[i] >= '0') && (aStr[i] <= '9') )
224 break;
225 else if ( aStr[i] == '-' )
227 bNegative = true;
228 break;
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') )
241 break;
242 else if ( aStr[i] == '-' )
244 bNegative = true;
245 break;
251 else
253 if ( !aStr1.isEmpty() && aStr1[0] == '-')
254 bNegative = true;
255 if ( !aStrNum.isEmpty() && aStrNum[0] == '-') // For non-mixed fractions
256 bNegative = true;
259 // remove all unwanted characters
260 // For whole number
261 for (sal_Int32 i=0; i < aStr1.getLength(); )
263 if ( (aStr1[i] >= '0') && (aStr1[i] <= '9') )
264 i++;
265 else
266 aStr1.remove( i, 1 );
268 // For decimal
269 if (!bFrac) {
270 for (sal_Int32 i=0; i < aStr2.getLength(); )
272 if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
273 ++i;
274 else
275 aStr2.remove(i, 1);
278 else {
279 // for numerator
280 for (sal_Int32 i=0; i < aStrNum.getLength(); )
282 if ((aStrNum[i] >= '0') && (aStrNum[i] <= '9'))
283 ++i;
284 else
285 aStrNum.remove(i, 1);
287 // for denominator
288 for (sal_Int32 i=0; i < aStrDenom.getLength(); )
290 if ((aStrDenom[i] >= '0') && (aStrDenom[i] <= '9'))
291 ++i;
292 else
293 aStrDenom.remove(i, 1);
298 if ( !bFrac && aStr1.isEmpty() && aStr2.isEmpty() )
299 return false;
300 else if ( bFrac && aStr1.isEmpty() && (aStrNum.isEmpty() || aStrDenom.isEmpty()) )
301 return false;
303 if ( aStr1.isEmpty() )
304 aStr1 = "0";
305 if ( bNegative )
306 aStr1.insert(0, "-");
308 // Convert fractional strings
309 if (bFrac) {
310 // Convert to fraction
311 sal_Int64 nWholeNum = o3tl::toInt64(aStr1);
312 aStr1.setLength(0);
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('.');
320 if ( nDecPos >= 0)
322 aStr1.append(aStrFrac.getStr(), nDecPos);
323 aStr2.append(aStrFrac.getStr()+nDecPos+1);
325 else
326 aStr1 = aStrFrac;
329 // prune and round fraction
330 bool bRound = false;
331 if (aStr2.getLength() > nDecDigits)
333 if (aStr2[nDecDigits] >= '5')
334 bRound = true;
335 string::truncateToLength(aStr2, nDecDigits);
337 if (aStr2.getLength() < nDecDigits)
338 string::padToLength(aStr2, nDecDigits, '0');
340 aStr = aStr1 + aStr2;
342 // check range
343 nValue = aStr.toInt64();
344 if( nValue == 0 )
346 // check if string is equivalent to zero
347 sal_Int32 nIndex = bNegative ? 1 : 0;
348 while (nIndex < aStr.getLength() && aStr[nIndex] == '0')
349 ++nIndex;
350 if( nIndex < aStr.getLength() )
352 rValue = bNegative ? SAL_MIN_INT64 : SAL_MAX_INT64;
353 return true;
356 if (bRound)
358 if ( !bNegative )
359 nValue++;
360 else
361 nValue--;
364 rValue = nValue;
366 return true;
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();
393 else
395 aBuf.append( pBuffer + nIndex );
396 nIndex = -1;
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,
405 Edit* pEdit )
407 bool bChangeDec = (rOldDecSep != rNewDecSep);
408 bool bChangeTh = (rOldThSep != rNewThSep );
410 if( !(bChangeDec || bChangeTh) )
411 return;
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);
420 if( pCombo )
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 );
434 if( bUpdateMode )
435 pEdit->SetUpdateMode( bUpdateMode );
438 } // namespace
440 FormatterBase::FormatterBase(Edit* pField)
442 mpField = pField;
443 mpLocaleDataWrapper = nullptr;
444 mbReformat = false;
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
467 // language tag
468 mpLocaleDataWrapper.reset();
471 const LocaleDataWrapper& FormatterBase::GetLocaleDataWrapper() const
473 return ImplGetLocaleDataWrapper();
476 void FormatterBase::Reformat()
480 void FormatterBase::ReformatAll()
482 Reformat();
485 void FormatterBase::SetStrictFormat( bool bStrict )
487 if ( bStrict != mbStrictFormat )
489 mbStrictFormat = bStrict;
490 if ( mbStrictFormat )
491 ReformatAll();
495 const lang::Locale& FormatterBase::GetLocale() const
497 if ( mpField )
498 return mpField->GetSettings().GetLanguageTag().getLocale();
499 else
500 return Application::GetSettings().GetLanguageTag().getLocale();
503 const LanguageTag& FormatterBase::GetLanguageTag() const
505 if ( mpField )
506 return mpField->GetSettings().GetLanguageTag();
507 else
508 return Application::GetSettings().GetLanguageTag();
511 void FormatterBase::ImplSetText( const OUString& rText, Selection const * pNewSelection )
513 if ( mpField )
515 if (pNewSelection)
516 mpField->SetText(rText, *pNewSelection);
517 else
519 Selection aSel = mpField->GetSelection();
520 aSel.Min() = aSel.Max();
521 mpField->SetText(rText, aSel);
523 MarkToBeReformatted( false );
527 void FormatterBase::SetEmptyFieldValue()
529 if ( mpField )
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)
541 mbFormatting = true;
542 ImplSetText(CreateFieldText(mnLastValue), pNewSelection);
543 mbFormatting = false;
546 void NumericFormatter::ImplNumericReformat()
548 mnLastValue = GetValue();
549 FormatValue();
552 NumericFormatter::NumericFormatter(Edit* pEdit)
553 : FormatterBase(pEdit)
554 , mnLastValue(0)
555 , mnMin(0)
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)
560 , mnSpinSize(1)
561 // for fields
562 , mnFirst(mnMin)
563 , mnLast(mnMax)
564 , mnDecimalDigits(0)
565 , mbThousandSep(true)
567 ReformatAll();
570 NumericFormatter::~NumericFormatter()
574 void NumericFormatter::SetMin( sal_Int64 nNewMin )
576 mnMin = nNewMin;
577 if ( !IsEmptyFieldValue() )
578 ReformatAll();
581 void NumericFormatter::SetMax( sal_Int64 nNewMax )
583 mnMax = nNewMax;
584 if ( !IsEmptyFieldValue() )
585 ReformatAll();
588 void NumericFormatter::SetUseThousandSep( bool bValue )
590 mbThousandSep = bValue;
591 ReformatAll();
594 void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits )
596 mnDecimalDigits = nDigits;
597 ReformatAll();
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;
616 if ( GetField() )
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);
634 else
635 return mnLastValue;
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)
647 sal_Int64 nValue;
649 if (ImplNumericGetValue(rStr, nValue, GetDecimalDigits(),
650 Application::GetSettings().GetNeutralLocaleDataWrapper()))
652 ImplNewFieldValue(nValue);
654 else
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
663 return mnLastValue;
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 );
683 if( nValue < 0 )
685 sal_Int64 nHalf = nFactor / 2;
686 return ((nValue - nHalf) / nFactor );
688 else
690 sal_Int64 nHalf = nFactor / 2;
691 return ((nValue + nHalf) / nFactor );
695 void NumericFormatter::Reformat()
697 if ( !GetField() )
698 return;
700 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
701 return;
703 ImplNumericReformat();
706 void NumericFormatter::FieldUp()
708 sal_Int64 nValue = GetValue();
709 sal_Int64 nRemainder = nValue % mnSpinSize;
710 if (nValue >= 0)
711 nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue + mnSpinSize - nRemainder;
712 else
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;
724 if (nValue >= 0)
725 nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - nRemainder;
726 else
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 )
746 if ( !GetField() )
747 return;
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
779 if (nValue > mnMax)
780 nValue = mnMax;
781 else if (nValue < mnMin)
782 nValue = mnMin;
783 return nValue;
786 namespace
788 Size calcMinimumSize(const Edit &rSpinField, const NumericFormatter &rFormatter)
790 OUStringBuffer aBuf;
791 sal_Int32 nTextLen;
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()));
797 aBuf.setLength(0);
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()));
803 aBuf.setLength(0);
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();
810 if (nDigits)
812 sBuf.append('.');
813 string::padToLength(aBuf, aBuf.getLength() + nDigits, '9');
815 aMaxTextSize = rSpinField.CalcMinimumSizeForText(sBuf.makeStringAndClear());
816 aRet.setWidth( std::min(aRet.Width(), aMaxTextSize.Width()) );
818 return aRet;
822 NumericBox::NumericBox(vcl::Window* pParent, WinBits nWinStyle)
823 : ComboBox(pParent, nWinStyle)
824 , NumericFormatter(this)
826 Reformat();
827 if ( !(nWinStyle & WB_HIDE ) )
828 Show();
831 void NumericBox::dispose()
833 ClearField();
834 ComboBox::dispose();
837 Size NumericBox::CalcMinimumSize() const
839 Size aRet(calcMinimumSize(*this, *this));
841 if (IsDropDownBox())
843 Size aComboSugg(ComboBox::CalcMinimumSize());
844 aRet.setWidth( std::max(aRet.Width(), aComboSugg.Width()) );
845 aRet.setHeight( std::max(aRet.Height(), aComboSugg.Height()) );
848 return aRet;
851 bool NumericBox::PreNotify( NotifyEvent& rNEvt )
853 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
855 if ( ImplNumericProcessKeyInput( *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
856 return true;
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()) )
869 Reformat();
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 );
887 ReformatAll();
891 void NumericBox::Modify()
893 MarkToBeReformatted( true );
894 ComboBox::Modify();
897 void NumericBox::ImplNumericReformat( const OUString& rStr, sal_Int64& rValue,
898 OUString& rOutStr )
900 if (ImplNumericGetValue(rStr, rValue, GetDecimalDigits(), ImplGetLocaleDataWrapper()))
902 sal_Int64 nTempVal = ClipAgainstMinMax(rValue);
903 rOutStr = CreateFieldText( nTempVal );
907 void NumericBox::ReformatAll()
909 sal_Int64 nValue;
910 OUString aStr;
911 SetUpdateMode( false );
912 sal_Int32 nEntryCount = GetEntryCount();
913 for ( sal_Int32 i=0; i < nEntryCount; i++ )
915 ImplNumericReformat( GetEntry( i ), nValue, aStr );
916 RemoveEntryAt(i);
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)
932 // fetch unit text
933 OUStringBuffer aStr;
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) )
938 aStr.insert(0, c);
939 else
941 if (!aStr.isEmpty())
942 break;
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)
956 return elem.first;
959 return OUString();
962 namespace
964 FieldUnit StringToMetric(const OUString &rMetricString)
966 // return FieldUnit
967 OUString aStr = rMetricString.toAsciiLowerCase().replaceAll(" ", "");
968 for (auto const& elem : ImplGetCleanedFieldUnits())
970 if ( elem.first == aStr )
971 return elem.second;
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 )
986 switch( meUnit )
988 case MapUnit::Map100thMM :
989 nDecDigits -= 2;
990 return FieldUnit::MM;
991 case MapUnit::Map10thMM :
992 nDecDigits -= 1;
993 return FieldUnit::MM;
994 case MapUnit::MapMM :
995 return FieldUnit::MM;
996 case MapUnit::MapCM :
997 return FieldUnit::CM;
998 case MapUnit::Map1000thInch :
999 nDecDigits -= 3;
1000 return FieldUnit::INCH;
1001 case MapUnit::Map100thInch :
1002 nDecDigits -= 2;
1003 return FieldUnit::INCH;
1004 case MapUnit::Map10thInch :
1005 nDecDigits -= 1;
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;
1013 default:
1014 OSL_FAIL( "default eInUnit" );
1015 break;
1017 return FieldUnit::NONE;
1020 static double nonValueDoubleToValueDouble( double nValue )
1022 return std::isfinite( nValue ) ? nValue : 0.0;
1025 namespace vcl
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));
1032 sal_Int64 nLong ;
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;
1039 else
1040 nLong = static_cast<sal_Int64>( std::round(nDouble) );
1042 return nLong;
1046 namespace {
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 )
1061 if ( nDigits < 0 )
1063 while ( nDigits )
1065 nValue += 5;
1066 nValue /= 10;
1067 nDigits++;
1070 else
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);
1082 return nValue;
1087 namespace vcl
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" );
1095 return nValue;
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)
1104 return nValue;
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;
1124 nValue += nDiv / 2;
1125 nValue /= nDiv;
1127 else
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);
1136 return nValue;
1139 double ConvertDoubleValue(double nValue, sal_uInt16 nDigits,
1140 MapUnit eInUnit, FieldUnit eOutUnit)
1142 if ( !checkConversionUnits(eInUnit, eOutUnit) )
1144 OSL_FAIL( "invalid parameters" );
1145 return nValue;
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" );
1170 return nValue;
1173 tools::Long nDecDigits = nDigits;
1174 FieldUnit eFieldUnit = ImplMap2FieldUnit( eOutUnit, nDecDigits );
1176 if ( nDecDigits < 0 )
1178 nValue *= ImplPower10(-nDecDigits);
1180 else
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);
1192 return nValue;
1196 namespace vcl
1198 bool TextToValue(const OUString& rStr, double& rValue, sal_Int64 nBaseValue,
1199 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper, FieldUnit eUnit)
1201 // Get value
1202 sal_Int64 nValue;
1203 if ( !ImplNumericGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper ) )
1204 return false;
1206 // Determine unit
1207 FieldUnit eEntryUnit = ImplMetricGetUnit( rStr );
1209 // Recalculate unit
1210 // caution: conversion to double loses precision
1211 rValue = vcl::ConvertDoubleValue(static_cast<double>(nValue), nBaseValue, nDecDigits, eEntryUnit, eUnit);
1213 return true;
1217 void MetricFormatter::ImplMetricReformat( const OUString& rStr, double& rValue, OUString& rOutStr )
1219 if (!vcl::TextToValue(rStr, rValue, 0, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit))
1220 return;
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;
1248 else
1249 meUnit = eNewUnit;
1250 ReformatAll();
1253 void MetricFormatter::SetCustomUnitText( const OUString& rStr )
1255 maCustomUnitText = rStr;
1256 ReformatAll();
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;
1279 else
1281 OUString aSuffix = ImplMetricToString( meUnit );
1282 if (meUnit != FieldUnit::NONE && meUnit != FieldUnit::DEGREE && meUnit != FieldUnit::INCH && meUnit != FieldUnit::FOOT)
1283 aStr += " ";
1284 if (meUnit == FieldUnit::INCH)
1286 OUString sDoublePrime = u"\u2033";
1287 if (aSuffix != "\"" && aSuffix != sDoublePrime)
1288 aStr += " ";
1289 else
1290 aSuffix = sDoublePrime;
1292 else if (meUnit == FieldUnit::FOOT)
1294 OUString sPrime = u"\u2032";
1295 if (aSuffix != "'" && aSuffix != sPrime)
1296 aStr += " ";
1297 else
1298 aSuffix = sPrime;
1301 assert(meUnit != FieldUnit::PERCENT);
1302 aStr += aSuffix;
1304 return aStr;
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
1316 double nTempValue;
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()
1373 if ( !GetField() )
1374 return;
1376 OUString aText = GetField()->GetText();
1378 OUString aStr;
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 );
1388 else
1389 SetValue( mnLastValue );
1392 sal_Int64 MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit ) const
1394 // convert to requested units
1395 return vcl::ConvertValue(0/*nCorrectedValue*/, 0, GetDecimalDigits(),
1396 meUnit, eOutUnit);
1399 MetricField::MetricField(vcl::Window* pParent, WinBits nWinStyle)
1400 : SpinField(pParent, nWinStyle, WindowType::METRICFIELD)
1401 , MetricFormatter(this)
1403 Reformat();
1406 void MetricField::dispose()
1408 ClearField();
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());
1423 else
1424 return SpinField::set_property(rKey, rValue);
1425 return true;
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 )
1446 // convert
1447 nNewFirst = vcl::ConvertValue(nNewFirst, 0, GetDecimalDigits(), eInUnit, meUnit);
1448 mnFirst = nNewFirst;
1451 sal_Int64 MetricField::GetFirst( FieldUnit eOutUnit ) const
1453 // convert
1454 return vcl::ConvertValue(mnFirst, 0, GetDecimalDigits(), meUnit, eOutUnit);
1457 void MetricField::SetLast( sal_Int64 nNewLast, FieldUnit eInUnit )
1459 // convert
1460 nNewLast = vcl::ConvertValue(nNewLast, 0, GetDecimalDigits(), eInUnit, meUnit);
1461 mnLast = nNewLast;
1464 sal_Int64 MetricField::GetLast( FieldUnit eOutUnit ) const
1466 // convert
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() ) )
1475 return true;
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()) )
1488 Reformat();
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 );
1506 ReformatAll();
1510 void MetricField::Modify()
1512 MarkToBeReformatted( true );
1513 SpinField::Modify();
1516 void MetricField::Up()
1518 FieldUp();
1519 SpinField::Up();
1522 void MetricField::Down()
1524 FieldDown();
1525 SpinField::Down();
1528 void MetricField::First()
1530 FieldFirst();
1531 SpinField::First();
1534 void MetricField::Last()
1536 FieldLast();
1537 SpinField::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)
1560 Reformat();
1563 void MetricBox::dispose()
1565 ClearField();
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()) );
1580 return aRet;
1583 bool MetricBox::PreNotify( NotifyEvent& rNEvt )
1585 if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1587 if ( ImplMetricProcessKeyInput( *rNEvt.GetKeyEvent(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1588 return true;
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()) )
1601 Reformat();
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 );
1619 ReformatAll();
1623 void MetricBox::Modify()
1625 MarkToBeReformatted( true );
1626 ComboBox::Modify();
1629 void MetricBox::ReformatAll()
1631 double nValue;
1632 OUString aStr;
1633 SetUpdateMode( false );
1634 sal_Int32 nEntryCount = GetEntryCount();
1635 for ( sal_Int32 i=0; i < nEntryCount; i++ )
1637 ImplMetricReformat( GetEntry( i ), nValue, aStr );
1638 RemoveEntryAt(i);
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 )
1655 // fetch number
1656 return ImplNumericGetValue( rStr, rValue, nDecDigits, rWrapper, true );
1659 void CurrencyFormatter::ImplCurrencyReformat( const OUString& rStr, OUString& rOutStr )
1661 sal_Int64 nValue;
1662 if ( !ImplNumericGetValue( rStr, nValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), true ) )
1663 return;
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);
1702 else
1703 return mnLastValue;
1706 void CurrencyFormatter::Reformat()
1708 if ( !GetField() )
1709 return;
1711 OUString aStr;
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;
1721 else
1722 SetValue( mnLastValue );
1725 CurrencyField::CurrencyField(vcl::Window* pParent, WinBits nWinStyle)
1726 : SpinField(pParent, nWinStyle)
1727 , CurrencyFormatter(this)
1729 Reformat();
1732 void CurrencyField::dispose()
1734 ClearField();
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() ) )
1743 return true;
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()) )
1756 Reformat();
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 );
1774 ReformatAll();
1778 void CurrencyField::Modify()
1780 MarkToBeReformatted( true );
1781 SpinField::Modify();
1784 void CurrencyField::Up()
1786 FieldUp();
1787 SpinField::Up();
1790 void CurrencyField::Down()
1792 FieldDown();
1793 SpinField::Down();
1796 void CurrencyField::First()
1798 FieldFirst();
1799 SpinField::First();
1802 void CurrencyField::Last()
1804 FieldLast();
1805 SpinField::Last();
1808 CurrencyBox::CurrencyBox(vcl::Window* pParent, WinBits nWinStyle)
1809 : ComboBox(pParent, nWinStyle)
1810 , CurrencyFormatter(this)
1812 Reformat();
1815 void CurrencyBox::dispose()
1817 ClearField();
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() ) )
1826 return true;
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()) )
1839 Reformat();
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 );
1857 ReformatAll();
1861 void CurrencyBox::Modify()
1863 MarkToBeReformatted( true );
1864 ComboBox::Modify();
1867 void CurrencyBox::ReformatAll()
1869 OUString aStr;
1870 SetUpdateMode( false );
1871 sal_Int32 nEntryCount = GetEntryCount();
1872 for ( sal_Int32 i=0; i < nEntryCount; i++ )
1874 ImplCurrencyReformat( GetEntry( i ), aStr );
1875 RemoveEntryAt(i);
1876 InsertEntry( aStr, i );
1878 CurrencyFormatter::Reformat();
1879 SetUpdateMode( true );
1882 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */