build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / control / field.cxx
blob50c83c1b20d816a4a9a59dbcdce847addb604367
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 <comphelper/processfactory.hxx>
23 #include <comphelper/string.hxx>
25 #include "tools/debug.hxx"
27 #include "tools/rc.h"
29 #include <vcl/dialog.hxx>
30 #include <vcl/field.hxx>
31 #include <vcl/event.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/settings.hxx>
35 #include "svids.hrc"
36 #include "svdata.hxx"
38 #include "i18nutil/unicode.hxx"
40 #include "rtl/math.hxx"
42 #include <unotools/localedatawrapper.hxx>
44 using namespace ::com::sun::star;
45 using namespace ::comphelper;
47 namespace
50 #define FORMAT_NUMERIC 1
51 #define FORMAT_METRIC 2
52 #define FORMAT_CURRENCY 3
54 sal_Int64 ImplPower10( sal_uInt16 n )
56 sal_uInt16 i;
57 sal_Int64 nValue = 1;
59 for ( i=0; i < n; i++ )
60 nValue *= 10;
62 return nValue;
65 bool ImplNumericProcessKeyInput( Edit*, const KeyEvent& rKEvt,
66 bool bStrictFormat, bool bThousandSep,
67 const LocaleDataWrapper& rLocaleDataWrappper )
69 if ( !bStrictFormat )
70 return false;
71 else
73 sal_Unicode cChar = rKEvt.GetCharCode();
74 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
76 if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
77 (nGroup == KEYGROUP_MISC) ||
78 ((cChar >= '0') && (cChar <= '9')) ||
79 string::equals(rLocaleDataWrappper.getNumDecimalSep(), cChar) ||
80 (bThousandSep && string::equals(rLocaleDataWrappper.getNumThousandSep(), cChar)) ||
81 (cChar == '-') )
82 return false;
83 else
84 return true;
88 bool ImplNumericGetValue( const OUString& rStr, sal_Int64& rValue,
89 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrappper,
90 bool bCurrency = false )
92 OUString aStr = rStr;
93 OUStringBuffer aStr1, aStr2, aStrFrac, aStrNum, aStrDenom;
94 bool bNegative = false;
95 bool bFrac = false;
96 sal_Int32 nDecPos, nFracDivPos, nFracNumPos;
97 sal_Int64 nValue;
99 // react on empty string
100 if ( rStr.isEmpty() )
101 return false;
103 // remove leading and trailing spaces
104 aStr = aStr.trim();
107 // find position of decimal point
108 nDecPos = aStr.indexOf( rLocaleDataWrappper.getNumDecimalSep() );
109 // find position of fraction
110 nFracDivPos = aStr.indexOf( '/' );
112 // parse fractional strings
113 if (nFracDivPos > 0)
115 bFrac = true;
116 nFracNumPos = aStr.lastIndexOf(' ', nFracDivPos);
118 // If in "a b/c" format.
119 if(nFracNumPos != -1 )
121 aStr1.append(aStr.getStr(), nFracNumPos);
122 aStrNum.append(aStr.getStr()+nFracNumPos+1, nFracDivPos-nFracNumPos-1);
123 aStrDenom.append(aStr.getStr()+nFracDivPos+1);
125 // "a/b" format, or not a fraction at all
126 else
128 aStrNum.append(aStr.getStr(), nFracDivPos);
129 aStrDenom.append(aStr.getStr()+nFracDivPos+1);
133 // parse decimal strings
134 else if ( nDecPos >= 0)
136 aStr1.append(aStr.getStr(), nDecPos);
137 aStr2.append(aStr.getStr()+nDecPos+1);
139 else
140 aStr1 = aStr;
142 // negative?
143 if ( bCurrency )
145 if ( aStr.startsWith("(") && aStr.endsWith(")") )
146 bNegative = true;
147 if ( !bNegative )
149 for (sal_Int32 i=0; i < aStr.getLength(); i++ )
151 if ( (aStr[i] >= '0') && (aStr[i] <= '9') )
152 break;
153 else if ( aStr[i] == '-' )
155 bNegative = true;
156 break;
160 if ( !bNegative && bCurrency && !aStr.isEmpty() )
162 sal_uInt16 nFormat = rLocaleDataWrappper.getCurrNegativeFormat();
163 if ( (nFormat == 3) || (nFormat == 6) || // $1- || 1-$
164 (nFormat == 7) || (nFormat == 10) ) // 1$- || 1 $-
166 for (sal_Int32 i = aStr.getLength()-1; i > 0; --i )
168 if ( (aStr[i] >= '0') && (aStr[i] <= '9') )
169 break;
170 else if ( aStr[i] == '-' )
172 bNegative = true;
173 break;
179 else
181 if ( !aStr1.isEmpty() && aStr1[0] == '-')
182 bNegative = true;
183 if ( !aStrNum.isEmpty() && aStrNum[0] == '-') // For non-mixed fractions
184 bNegative = true;
187 // remove all unwanted charaters
188 // For whole number
189 for (sal_Int32 i=0; i < aStr1.getLength(); )
191 if ( (aStr1[i] >= '0') && (aStr1[i] <= '9') )
192 i++;
193 else
194 aStr1.remove( i, 1 );
196 // For decimal
197 if (!bFrac) {
198 for (sal_Int32 i=0; i < aStr2.getLength(); )
200 if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
201 ++i;
202 else
203 aStr2.remove(i, 1);
206 else {
207 // for numerator
208 for (sal_Int32 i=0; i < aStrNum.getLength(); )
210 if ((aStrNum[i] >= '0') && (aStrNum[i] <= '9'))
211 ++i;
212 else
213 aStrNum.remove(i, 1);
215 // for denominator
216 for (sal_Int32 i=0; i < aStrDenom.getLength(); )
218 if ((aStrDenom[i] >= '0') && (aStrDenom[i] <= '9'))
219 ++i;
220 else
221 aStrDenom.remove(i, 1);
226 if ( !bFrac && aStr1.isEmpty() && aStr2.isEmpty() )
227 return false;
228 else if ( bFrac && aStr1.isEmpty() && (aStrNum.isEmpty() || aStrDenom.isEmpty()) )
229 return false;
231 if ( aStr1.isEmpty() )
232 aStr1 = "0";
233 if ( bNegative )
234 aStr1.insert(0, "-");
236 // Convert fractional strings
237 if (bFrac) {
238 // Convert to fraction
239 sal_Int64 nWholeNum = aStr1.makeStringAndClear().toInt64();
240 sal_Int64 nNum = aStrNum.makeStringAndClear().toInt64();
241 sal_Int64 nDenom = aStrDenom.makeStringAndClear().toInt64();
242 if (nDenom == 0) return false; // Division by zero
243 double nFrac2Dec = nWholeNum + (double)nNum/nDenom; // Convert to double for floating point precision
244 aStrFrac.append(nFrac2Dec);
245 // Reconvert division result to string and parse
246 nDecPos = aStrFrac.indexOf( rLocaleDataWrappper.getNumDecimalSep() );
247 if ( nDecPos >= 0)
249 aStr1.append(aStrFrac.getStr(), nDecPos);
250 aStr2.append(aStrFrac.getStr()+nDecPos+1);
252 else
253 aStr1 = aStrFrac;
256 // prune and round fraction
257 bool bRound = false;
258 if (aStr2.getLength() > nDecDigits)
260 if (aStr2[nDecDigits] >= '5')
261 bRound = true;
262 string::truncateToLength(aStr2, nDecDigits);
264 if (aStr2.getLength() < nDecDigits)
265 string::padToLength(aStr2, nDecDigits, '0');
267 aStr = aStr1.makeStringAndClear() + aStr2.makeStringAndClear();
269 // check range
270 nValue = aStr.toInt64();
271 if( nValue == 0 )
273 // check if string is equivalent to zero
274 sal_Int16 nIndex = bNegative ? 1 : 0;
275 while (nIndex < aStr.getLength() && aStr[nIndex] == '0')
276 ++nIndex;
277 if( nIndex < aStr.getLength() )
279 rValue = bNegative ? SAL_MIN_INT64 : SAL_MAX_INT64;
280 return true;
283 if (bRound)
285 if ( !bNegative )
286 nValue++;
287 else
288 nValue--;
291 rValue = nValue;
293 return true;
296 void ImplUpdateSeparatorString( OUString& io_rText,
297 const OUString& rOldDecSep, const OUString& rNewDecSep,
298 const OUString& rOldThSep, const OUString& rNewThSep )
300 OUStringBuffer aBuf( io_rText.getLength() );
301 sal_Int32 nIndexDec = 0, nIndexTh = 0, nIndex = 0;
303 const sal_Unicode* pBuffer = io_rText.getStr();
304 while( nIndex != -1 )
306 nIndexDec = io_rText.indexOf( rOldDecSep, nIndex );
307 nIndexTh = io_rText.indexOf( rOldThSep, nIndex );
308 if( (nIndexTh != -1 && nIndexDec != -1 && nIndexTh < nIndexDec )
309 || (nIndexTh != -1 && nIndexDec == -1)
312 aBuf.append( pBuffer + nIndex, nIndexTh - nIndex );
313 aBuf.append( rNewThSep );
314 nIndex = nIndexTh + rOldThSep.getLength();
316 else if( nIndexDec != -1 )
318 aBuf.append( pBuffer + nIndex, nIndexDec - nIndex );
319 aBuf.append( rNewDecSep );
320 nIndex = nIndexDec + rOldDecSep.getLength();
322 else
324 aBuf.append( pBuffer + nIndex );
325 nIndex = -1;
329 io_rText = aBuf.makeStringAndClear();
332 void ImplUpdateSeparators( const OUString& rOldDecSep, const OUString& rNewDecSep,
333 const OUString& rOldThSep, const OUString& rNewThSep,
334 Edit* pEdit )
336 bool bChangeDec = (rOldDecSep != rNewDecSep);
337 bool bChangeTh = (rOldThSep != rNewThSep );
339 if( bChangeDec || bChangeTh )
341 bool bUpdateMode = pEdit->IsUpdateMode();
342 pEdit->SetUpdateMode( false );
343 OUString aText = pEdit->GetText();
344 ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep );
345 pEdit->SetText( aText );
347 ComboBox* pCombo = dynamic_cast<ComboBox*>(pEdit);
348 if( pCombo )
350 // update box entries
351 sal_Int32 nEntryCount = pCombo->GetEntryCount();
352 for ( sal_Int32 i=0; i < nEntryCount; i++ )
354 aText = pCombo->GetEntry( i );
355 void* pEntryData = pCombo->GetEntryData( i );
356 ImplUpdateSeparatorString( aText, rOldDecSep, rNewDecSep, rOldThSep, rNewThSep );
357 pCombo->RemoveEntryAt(i);
358 pCombo->InsertEntry( aText, i );
359 pCombo->SetEntryData( i, pEntryData );
362 if( bUpdateMode )
363 pEdit->SetUpdateMode( bUpdateMode );
367 } // namespace
369 FormatterBase::FormatterBase()
371 mpField = nullptr;
372 mpLocaleDataWrapper = nullptr;
373 mbReformat = false;
374 mbStrictFormat = false;
375 mbEmptyFieldValue = false;
376 mbEmptyFieldValueEnabled = false;
377 mbDefaultLocale = true;
380 FormatterBase::~FormatterBase()
384 LocaleDataWrapper& FormatterBase::ImplGetLocaleDataWrapper() const
386 if ( !mpLocaleDataWrapper )
388 const_cast<FormatterBase*>(this)->mpLocaleDataWrapper.reset( new LocaleDataWrapper( GetLanguageTag() ) );
390 return *mpLocaleDataWrapper;
393 const LocaleDataWrapper& FormatterBase::GetLocaleDataWrapper() const
395 return ImplGetLocaleDataWrapper();
398 void FormatterBase::Reformat()
402 void FormatterBase::ReformatAll()
404 Reformat();
407 void FormatterBase::SetStrictFormat( bool bStrict )
409 if ( bStrict != mbStrictFormat )
411 mbStrictFormat = bStrict;
412 if ( mbStrictFormat )
413 ReformatAll();
417 void FormatterBase::SetLocale( const lang::Locale& rLocale )
419 ImplGetLocaleDataWrapper().setLanguageTag( LanguageTag( rLocale) );
420 mbDefaultLocale = false;
421 ReformatAll();
424 const lang::Locale& FormatterBase::GetLocale() const
426 if ( !mpLocaleDataWrapper || mbDefaultLocale )
428 if ( mpField )
429 return mpField->GetSettings().GetLanguageTag().getLocale();
430 else
431 return Application::GetSettings().GetLanguageTag().getLocale();
434 return mpLocaleDataWrapper->getLanguageTag().getLocale();
437 const LanguageTag& FormatterBase::GetLanguageTag() const
439 if ( !mpLocaleDataWrapper || mbDefaultLocale )
441 if ( mpField )
442 return mpField->GetSettings().GetLanguageTag();
443 else
444 return Application::GetSettings().GetLanguageTag();
447 return mpLocaleDataWrapper->getLanguageTag();
450 const AllSettings& FormatterBase::GetFieldSettings() const
452 if ( mpField )
453 return mpField->GetSettings();
454 else
455 return Application::GetSettings();
458 void FormatterBase::ImplSetText( const OUString& rText, Selection* pNewSelection )
460 if ( mpField )
462 if ( pNewSelection )
463 mpField->SetText( rText, *pNewSelection );
464 else
466 Selection aSel = mpField->GetSelection();
467 aSel.Min() = aSel.Max();
468 mpField->SetText( rText, aSel );
471 MarkToBeReformatted( false );
475 void FormatterBase::SetEmptyFieldValue()
477 if ( mpField )
478 mpField->SetText( OUString() );
479 mbEmptyFieldValue = true;
482 bool FormatterBase::IsEmptyFieldValue() const
484 return (!mpField || mpField->GetText().isEmpty());
487 bool NumericFormatter::ImplNumericReformat( const OUString& rStr, sal_Int64& rValue,
488 OUString& rOutStr )
490 if ( !ImplNumericGetValue( rStr, rValue, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
491 return true;
492 else
494 sal_Int64 nTempVal = ClipAgainstMinMax(rValue);
496 rOutStr = CreateFieldText( nTempVal );
497 return true;
501 void NumericFormatter::ImplInit()
503 mnFieldValue = 0;
504 mnLastValue = 0;
505 mnMin = 0;
506 mnMax = SAL_MAX_INT32;
507 // a "large" value substantially smaller than SAL_MAX_INT64, to avoid
508 // overflow in computations using this "dummy" value
509 mnDecimalDigits = 2;
510 mnType = FORMAT_NUMERIC;
511 mbThousandSep = true;
512 mbShowTrailingZeros = true;
513 mbWrapOnLimits = false;
515 // for fields
516 mnSpinSize = 1;
517 mnFirst = mnMin;
518 mnLast = mnMax;
520 SetDecimalDigits( 0 );
523 NumericFormatter::NumericFormatter()
525 ImplInit();
528 NumericFormatter::~NumericFormatter()
532 void NumericFormatter::SetMin( sal_Int64 nNewMin )
534 mnMin = nNewMin;
535 if ( !IsEmptyFieldValue() )
536 ReformatAll();
539 void NumericFormatter::SetMax( sal_Int64 nNewMax )
541 mnMax = nNewMax;
542 if ( !IsEmptyFieldValue() )
543 ReformatAll();
546 void NumericFormatter::SetUseThousandSep( bool bValue )
548 mbThousandSep = bValue;
549 ReformatAll();
552 void NumericFormatter::SetDecimalDigits( sal_uInt16 nDigits )
554 mnDecimalDigits = nDigits;
555 ReformatAll();
558 void NumericFormatter::SetShowTrailingZeros( bool bShowTrailingZeros )
560 if ( mbShowTrailingZeros != bShowTrailingZeros )
562 mbShowTrailingZeros = bShowTrailingZeros;
563 ReformatAll();
568 void NumericFormatter::SetValue( sal_Int64 nNewValue )
570 SetUserValue( nNewValue );
571 mnFieldValue = mnLastValue;
572 SetEmptyFieldValueData( false );
575 OUString NumericFormatter::CreateFieldText( sal_Int64 nValue ) const
577 return OUString(ImplGetLocaleDataWrapper().getNum( nValue, GetDecimalDigits(), IsUseThousandSep(), IsShowTrailingZeros() ));
580 void NumericFormatter::ImplSetUserValue( sal_Int64 nNewValue, Selection* pNewSelection )
582 nNewValue = ClipAgainstMinMax(nNewValue);
583 mnLastValue = nNewValue;
585 if ( GetField() )
586 ImplSetText( CreateFieldText( nNewValue ), pNewSelection );
589 void NumericFormatter::SetUserValue( sal_Int64 nNewValue )
591 ImplSetUserValue( nNewValue );
594 sal_Int64 NumericFormatter::GetValue() const
596 if ( !GetField() )
597 return 0;
599 sal_Int64 nTempValue;
601 if ( ImplNumericGetValue( GetField()->GetText(), nTempValue,
602 GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
604 return ClipAgainstMinMax(nTempValue);
606 else
607 return mnLastValue;
610 bool NumericFormatter::IsValueModified() const
612 if ( ImplGetEmptyFieldValue() )
613 return !IsEmptyFieldValue();
614 else if ( GetValue() != mnFieldValue )
615 return true;
616 else
617 return false;
620 sal_Int64 NumericFormatter::Normalize( sal_Int64 nValue ) const
622 return (nValue * ImplPower10( GetDecimalDigits() ) );
625 sal_Int64 NumericFormatter::Denormalize( sal_Int64 nValue ) const
627 sal_Int64 nFactor = ImplPower10( GetDecimalDigits() );
629 if ((nValue < ( SAL_MIN_INT64 + nFactor )) ||
630 (nValue > ( SAL_MAX_INT64 - nFactor )))
632 return ( nValue / nFactor );
635 if( nValue < 0 )
637 sal_Int64 nHalf = nFactor / 2;
638 return ((nValue - nHalf) / nFactor );
640 else
642 sal_Int64 nHalf = nFactor / 2;
643 return ((nValue + nHalf) / nFactor );
647 void NumericFormatter::Reformat()
649 if ( !GetField() )
650 return;
652 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
653 return;
655 OUString aStr;
656 sal_Int64 nTemp = mnLastValue;
657 bool bOK = ImplNumericReformat( GetField()->GetText(), nTemp, aStr );
658 mnLastValue = nTemp;
659 if ( !bOK )
660 return;
662 if ( !aStr.isEmpty() )
663 ImplSetText( aStr );
664 else
665 SetValue( mnLastValue );
668 void NumericFormatter::FieldUp()
670 sal_Int64 nValue = GetValue();
671 sal_Int64 nRemainder = nValue % mnSpinSize;
672 if (nValue >= 0)
673 nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue + mnSpinSize - nRemainder;
674 else
675 nValue = (nRemainder == 0) ? nValue + mnSpinSize : nValue - nRemainder;
677 nValue = ClipAgainstMinMax(nValue);
679 ImplNewFieldValue( nValue );
682 void NumericFormatter::FieldDown()
684 sal_Int64 nValue = GetValue();
685 sal_Int64 nRemainder = nValue % mnSpinSize;
686 if (nValue >= 0)
687 nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - nRemainder;
688 else
689 nValue = (nRemainder == 0) ? nValue - mnSpinSize : nValue - mnSpinSize - nRemainder;
691 nValue = ClipAgainstMinMax(nValue);
693 ImplNewFieldValue( nValue );
696 void NumericFormatter::FieldFirst()
698 ImplNewFieldValue( mnFirst );
701 void NumericFormatter::FieldLast()
703 ImplNewFieldValue( mnLast );
706 void NumericFormatter::ImplNewFieldValue( sal_Int64 nNewValue )
708 if ( GetField() )
710 // !!! We should check why we do not validate in ImplSetUserValue() if the value was
711 // changed. This should be done there as well since otherwise the call to Modify would not
712 // be allowed. Anyway, the paths from ImplNewFieldValue, ImplSetUserValue, and ImplSetText
713 // should be checked and clearly traced (with comment) in order to find out what happens.
715 Selection aSelection = GetField()->GetSelection();
716 aSelection.Justify();
717 OUString aText = GetField()->GetText();
718 // leave it as is if selected until end
719 if ( (sal_Int32)aSelection.Max() == aText.getLength() )
721 if ( !aSelection.Len() )
722 aSelection.Min() = SELECTION_MAX;
723 aSelection.Max() = SELECTION_MAX;
726 sal_Int64 nOldLastValue = mnLastValue;
727 ImplSetUserValue( nNewValue, &aSelection );
728 mnLastValue = nOldLastValue;
730 // Modify during Edit is only set during KeyInput
731 if ( GetField()->GetText() != aText )
733 GetField()->SetModifyFlag();
734 GetField()->Modify();
739 sal_Int64 NumericFormatter::ClipAgainstMinMax(sal_Int64 nValue) const
741 if (nValue > mnMax)
742 nValue = mbWrapOnLimits ? ((nValue - mnMin) % (mnMax + 1)) + mnMin
743 : mnMax;
744 else if (nValue < mnMin)
745 nValue = mbWrapOnLimits ? ((nValue + mnMax + 1 - mnMin) % (mnMax + 1)) + mnMin
746 : mnMin;
747 return nValue;
750 NumericField::NumericField( vcl::Window* pParent, WinBits nWinStyle ) :
751 SpinField( pParent, nWinStyle )
753 SetField( this );
754 Reformat();
757 void NumericField::dispose()
759 NumericFormatter::SetField( nullptr );
760 SpinField::dispose();
763 bool NumericField::set_property(const OString &rKey, const OString &rValue)
765 if (rKey == "digits")
766 SetDecimalDigits(rValue.toInt32());
767 else if (rKey == "spin-size")
768 SetSpinSize(rValue.toInt32());
769 else if (rKey == "wrap")
770 mbWrapOnLimits = toBool(rValue);
771 else
772 return SpinField::set_property(rKey, rValue);
773 return true;
776 bool NumericField::PreNotify( NotifyEvent& rNEvt )
778 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
780 if ( ImplNumericProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
781 return true;
784 return SpinField::PreNotify( rNEvt );
787 bool NumericField::EventNotify( NotifyEvent& rNEvt )
789 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
790 MarkToBeReformatted( false );
791 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
793 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
794 Reformat();
797 return SpinField::EventNotify( rNEvt );
800 void NumericField::DataChanged( const DataChangedEvent& rDCEvt )
802 SpinField::DataChanged( rDCEvt );
804 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
806 OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
807 OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
808 if ( IsDefaultLocale() )
809 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
810 OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
811 OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
812 ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
813 ReformatAll();
817 void NumericField::Modify()
819 MarkToBeReformatted( true );
820 SpinField::Modify();
823 void NumericField::Up()
825 FieldUp();
826 SpinField::Up();
829 void NumericField::Down()
831 FieldDown();
832 SpinField::Down();
835 void NumericField::First()
837 FieldFirst();
838 SpinField::First();
841 void NumericField::Last()
843 FieldLast();
844 SpinField::Last();
847 namespace
849 Size calcMinimumSize(const Edit &rSpinField, const NumericFormatter &rFormatter)
851 OUStringBuffer aBuf;
852 sal_Int32 nTextLen;
854 nTextLen = OUString::number(rFormatter.GetMin()).getLength();
855 string::padToLength(aBuf, nTextLen, '9');
856 Size aMinTextSize = rSpinField.CalcMinimumSizeForText(
857 rFormatter.CreateFieldText(aBuf.makeStringAndClear().toInt64()));
859 nTextLen = OUString::number(rFormatter.GetMax()).getLength();
860 string::padToLength(aBuf, nTextLen, '9');
861 Size aMaxTextSize = rSpinField.CalcMinimumSizeForText(
862 rFormatter.CreateFieldText(aBuf.makeStringAndClear().toInt64()));
864 Size aRet(std::max(aMinTextSize.Width(), aMaxTextSize.Width()),
865 std::max(aMinTextSize.Height(), aMaxTextSize.Height()));
867 OUStringBuffer sBuf("999999999");
868 sal_uInt16 nDigits = rFormatter.GetDecimalDigits();
869 if (nDigits)
871 sBuf.append('.');
872 string::padToLength(aBuf, aBuf.getLength() + nDigits, '9');
874 aMaxTextSize = rSpinField.CalcMinimumSizeForText(sBuf.makeStringAndClear());
875 aRet.Width() = std::min(aRet.Width(), aMaxTextSize.Width());
877 return aRet;
881 Size NumericField::CalcMinimumSize() const
883 return calcMinimumSize(*this, *this);
886 NumericBox::NumericBox( vcl::Window* pParent, WinBits nWinStyle ) :
887 ComboBox( pParent, nWinStyle )
889 SetField( this );
890 Reformat();
891 if ( !(nWinStyle & WB_HIDE ) )
892 Show();
895 void NumericBox::dispose()
897 NumericFormatter::SetField( nullptr );
898 ComboBox::dispose();
901 Size NumericBox::CalcMinimumSize() const
903 Size aRet(calcMinimumSize(*this, *this));
905 if (IsDropDownBox())
907 Size aComboSugg(ComboBox::CalcMinimumSize());
908 aRet.Width() = std::max(aRet.Width(), aComboSugg.Width());
909 aRet.Height() = std::max(aRet.Height(), aComboSugg.Height());
912 return aRet;
915 bool NumericBox::PreNotify( NotifyEvent& rNEvt )
917 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
919 if ( ImplNumericProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
920 return true;
923 return ComboBox::PreNotify( rNEvt );
926 bool NumericBox::EventNotify( NotifyEvent& rNEvt )
928 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
929 MarkToBeReformatted( false );
930 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
932 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
933 Reformat();
936 return ComboBox::EventNotify( rNEvt );
939 void NumericBox::DataChanged( const DataChangedEvent& rDCEvt )
941 ComboBox::DataChanged( rDCEvt );
943 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
945 OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
946 OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
947 if ( IsDefaultLocale() )
948 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
949 OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
950 OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
951 ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
952 ReformatAll();
956 void NumericBox::Modify()
958 MarkToBeReformatted( true );
959 ComboBox::Modify();
962 void NumericBox::ReformatAll()
964 sal_Int64 nValue;
965 OUString aStr;
966 SetUpdateMode( false );
967 sal_Int32 nEntryCount = GetEntryCount();
968 for ( sal_Int32 i=0; i < nEntryCount; i++ )
970 ImplNumericReformat( GetEntry( i ), nValue, aStr );
971 RemoveEntryAt(i);
972 InsertEntry( aStr, i );
974 NumericFormatter::Reformat();
975 SetUpdateMode( true );
978 void NumericBox::InsertValue( sal_Int64 nValue, sal_Int32 nPos )
980 ComboBox::InsertEntry( CreateFieldText( nValue ), nPos );
983 static bool ImplMetricProcessKeyInput( Edit* pEdit, const KeyEvent& rKEvt,
984 bool, bool bUseThousandSep, const LocaleDataWrapper& rWrapper )
986 // no meaningfull strict format; therefore allow all characters
987 return ImplNumericProcessKeyInput( pEdit, rKEvt, false, bUseThousandSep, rWrapper );
990 static OUString ImplMetricGetUnitText(const OUString& rStr)
992 // fetch unit text
993 OUStringBuffer aStr;
994 for (sal_Int32 i = rStr.getLength()-1; i >= 0; --i)
996 sal_Unicode c = rStr[i];
997 if ( (c == '\'') || (c == '\"') || (c == '%' ) || unicode::isAlpha(c) || unicode::isControl(c) )
998 aStr.insert(0, c);
999 else
1001 if (!aStr.isEmpty())
1002 break;
1005 return aStr.makeStringAndClear();
1008 // #104355# support localized measurements
1010 static const OUString ImplMetricToString( FieldUnit rUnit )
1012 FieldUnitStringList* pList = ImplGetFieldUnits();
1013 if( pList )
1015 // return unit's default string (ie, the first one )
1016 for( FieldUnitStringList::const_iterator it = pList->begin(); it != pList->end(); ++it )
1018 if ( it->second == rUnit )
1019 return it->first;
1023 return OUString();
1026 static FieldUnit ImplStringToMetric(const OUString &rMetricString)
1028 FieldUnitStringList* pList = ImplGetCleanedFieldUnits();
1029 if( pList )
1031 // return FieldUnit
1032 OUString aStr = rMetricString.toAsciiLowerCase().replaceAll(" ", "");
1033 for( FieldUnitStringList::const_iterator it = pList->begin(); it != pList->end(); ++it )
1035 if ( it->first == aStr )
1036 return it->second;
1040 return FUNIT_NONE;
1043 static FieldUnit ImplMetricGetUnit(const OUString& rStr)
1045 OUString aStr = ImplMetricGetUnitText( rStr );
1046 return ImplStringToMetric( aStr );
1049 #define K *1000L
1050 #define M *1000000LL
1051 #define X *5280L
1053 // twip in km = 254 / 14 400 000 000
1054 // expressions too big for default size 32 bit need LL to avoid overflow
1056 static const sal_Int64 aImplFactor[FUNIT_LINE+1][FUNIT_LINE+1] =
1057 { /*
1058 mm/100 mm cm m km twip point pica inch foot mile char line */
1059 { 1, 100, 1 K, 100 K, 100 M, 2540, 2540, 2540, 2540,2540*12,2540*12 X , 53340, 396240},
1060 { 1, 1, 10, 1 K, 1 M, 2540, 2540, 2540, 2540,2540*12,2540*12 X , 5334, 396240},
1061 { 1, 1, 1, 100, 100 K, 254, 254, 254, 254, 254*12, 254*12 X , 5334, 39624},
1062 { 1, 1, 1, 1, 1 K, 254, 254, 254, 254, 254*12, 254*12 X , 533400, 39624},
1063 { 1, 1, 1, 1, 1, 254, 254, 254, 254, 254*12, 254*12 X ,533400 K, 39624},
1064 { 1440,144 K,144 K,14400 K,14400LL M, 1, 20, 240, 1440,1440*12,1440*12 X , 210, 3120},
1065 { 72, 7200, 7200, 720 K, 720 M, 1, 1, 12, 72, 72*12, 72*12 X , 210, 156},
1066 { 6, 600, 600, 60 K, 60 M, 1, 1, 1, 6, 6*12, 6*12 X , 210, 10},
1067 { 1, 100, 100, 10 K, 10 M, 1, 1, 1, 1, 12, 12 X , 210, 45},
1068 { 1, 100, 100, 10 K, 10 M, 1, 1, 1, 1, 1, 1 X , 210, 45},
1069 { 1, 100, 100, 10 K, 10 M, 1, 1, 1, 1, 1, 1 , 210, 45},
1070 { 144, 1440,14400, 14400, 14400, 1, 20, 240, 1440,1440*12, 1440*12 X, 1, 156 },
1071 { 720,72000,72000, 7200 K,7200LL M, 20, 10, 13, 11, 11*12, 11*12 X, 105, 1 }
1073 #undef X
1074 #undef M
1075 #undef K
1077 static FieldUnit eDefaultUnit = FUNIT_NONE;
1079 FieldUnit MetricField::GetDefaultUnit() { return eDefaultUnit; }
1080 void MetricField::SetDefaultUnit( FieldUnit meUnit ) { eDefaultUnit = meUnit; }
1082 static FieldUnit ImplMap2FieldUnit( MapUnit meUnit, long& nDecDigits )
1084 switch( meUnit )
1086 case MapUnit::Map100thMM :
1087 nDecDigits -= 2;
1088 return FUNIT_MM;
1089 case MapUnit::Map10thMM :
1090 nDecDigits -= 1;
1091 return FUNIT_MM;
1092 case MapUnit::MapMM :
1093 return FUNIT_MM;
1094 case MapUnit::MapCM :
1095 return FUNIT_CM;
1096 case MapUnit::Map1000thInch :
1097 nDecDigits -= 3;
1098 return FUNIT_INCH;
1099 case MapUnit::Map100thInch :
1100 nDecDigits -= 2;
1101 return FUNIT_INCH;
1102 case MapUnit::Map10thInch :
1103 nDecDigits -= 1;
1104 return FUNIT_INCH;
1105 case MapUnit::MapInch :
1106 return FUNIT_INCH;
1107 case MapUnit::MapPoint :
1108 return FUNIT_POINT;
1109 case MapUnit::MapTwip :
1110 return FUNIT_TWIP;
1111 default:
1112 OSL_FAIL( "default eInUnit" );
1113 break;
1115 return FUNIT_NONE;
1118 static double nonValueDoubleToValueDouble( double nValue )
1120 return rtl::math::isFinite( nValue ) ? nValue : 0.0;
1123 sal_Int64 MetricField::ConvertValue( sal_Int64 nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits,
1124 FieldUnit eInUnit, FieldUnit eOutUnit )
1126 double nDouble = nonValueDoubleToValueDouble( ConvertDoubleValue(
1127 (double)nValue, mnBaseValue, nDecDigits, eInUnit, eOutUnit ) );
1128 sal_Int64 nLong ;
1130 // caution: precision loss in double cast
1131 if ( nDouble <= (double)SAL_MIN_INT64 )
1132 nLong = SAL_MIN_INT64;
1133 else if ( nDouble >= (double)SAL_MAX_INT64 )
1134 nLong = SAL_MAX_INT64;
1135 else
1136 nLong = static_cast<sal_Int64>( nDouble );
1138 return nLong;
1141 sal_Int64 MetricField::ConvertValue( sal_Int64 nValue, sal_uInt16 nDigits,
1142 MapUnit eInUnit, FieldUnit eOutUnit )
1144 return static_cast<sal_Int64>(
1145 nonValueDoubleToValueDouble(
1146 ConvertDoubleValue( nValue, nDigits, eInUnit, eOutUnit ) ) );
1149 double MetricField::ConvertDoubleValue( double nValue, sal_Int64 mnBaseValue, sal_uInt16 nDecDigits,
1150 FieldUnit eInUnit, FieldUnit eOutUnit )
1152 if ( eInUnit != eOutUnit )
1154 sal_Int64 nMult = 1, nDiv = 1;
1156 if ( eInUnit == FUNIT_PERCENT )
1158 if ( (mnBaseValue <= 0) || (nValue <= 0) )
1159 return nValue;
1160 nDiv = 100 * ImplPower10(nDecDigits);
1162 nMult = mnBaseValue;
1164 else if ( eOutUnit == FUNIT_PERCENT ||
1165 eOutUnit == FUNIT_CUSTOM ||
1166 eOutUnit == FUNIT_NONE ||
1167 eOutUnit == FUNIT_DEGREE ||
1168 eOutUnit == FUNIT_SECOND ||
1169 eOutUnit == FUNIT_MILLISECOND ||
1170 eOutUnit == FUNIT_PIXEL ||
1171 eInUnit == FUNIT_CUSTOM ||
1172 eInUnit == FUNIT_NONE ||
1173 eInUnit == FUNIT_DEGREE ||
1174 eInUnit == FUNIT_MILLISECOND ||
1175 eInUnit == FUNIT_PIXEL )
1176 return nValue;
1177 else
1179 if ( eOutUnit == FUNIT_100TH_MM )
1180 eOutUnit = FUNIT_NONE;
1181 if ( eInUnit == FUNIT_100TH_MM )
1182 eInUnit = FUNIT_NONE;
1184 nDiv = aImplFactor[eInUnit][eOutUnit];
1185 nMult = aImplFactor[eOutUnit][eInUnit];
1187 SAL_WARN_IF( nMult <= 0, "vcl", "illegal *" );
1188 SAL_WARN_IF( nDiv <= 0, "vcl", "illegal /" );
1191 if ( nMult != 1 && nMult > 0 )
1192 nValue *= nMult;
1193 if ( nDiv != 1 && nDiv > 0 )
1195 nValue += ( nValue < 0 ) ? (-nDiv/2) : (nDiv/2);
1196 nValue /= nDiv;
1200 return nValue;
1203 double MetricField::ConvertDoubleValue( double nValue, sal_uInt16 nDigits,
1204 MapUnit eInUnit, FieldUnit eOutUnit )
1206 if ( eOutUnit == FUNIT_PERCENT ||
1207 eOutUnit == FUNIT_CUSTOM ||
1208 eOutUnit == FUNIT_NONE ||
1209 eInUnit == MapUnit::MapPixel ||
1210 eInUnit == MapUnit::MapSysFont ||
1211 eInUnit == MapUnit::MapAppFont ||
1212 eInUnit == MapUnit::MapRelative )
1214 OSL_FAIL( "invalid parameters" );
1215 return nValue;
1218 long nDecDigits = nDigits;
1219 FieldUnit eFieldUnit = ImplMap2FieldUnit( eInUnit, nDecDigits );
1221 if ( nDecDigits < 0 )
1223 while ( nDecDigits )
1225 nValue += 5;
1226 nValue /= 10;
1227 nDecDigits++;
1230 else
1232 nValue *= ImplPower10(nDecDigits);
1235 if ( eFieldUnit != eOutUnit )
1237 sal_Int64 nDiv = aImplFactor[eFieldUnit][eOutUnit];
1238 sal_Int64 nMult = aImplFactor[eOutUnit][eFieldUnit];
1240 SAL_WARN_IF( nMult <= 0, "vcl", "illegal *" );
1241 SAL_WARN_IF( nDiv <= 0, "vcl", "illegal /" );
1243 if ( nMult != 1 && nMult > 0)
1244 nValue *= nMult;
1245 if ( nDiv != 1 && nDiv > 0 )
1247 nValue += (nValue < 0) ? (-nDiv/2) : (nDiv/2);
1248 nValue /= nDiv;
1251 return nValue;
1254 double MetricField::ConvertDoubleValue( double nValue, sal_uInt16 nDigits,
1255 FieldUnit eInUnit, MapUnit eOutUnit )
1257 if ( eInUnit == FUNIT_PERCENT ||
1258 eInUnit == FUNIT_CUSTOM ||
1259 eInUnit == FUNIT_NONE ||
1260 eInUnit == FUNIT_DEGREE ||
1261 eInUnit == FUNIT_SECOND ||
1262 eInUnit == FUNIT_MILLISECOND ||
1263 eInUnit == FUNIT_PIXEL ||
1264 eOutUnit == MapUnit::MapPixel ||
1265 eOutUnit == MapUnit::MapSysFont ||
1266 eOutUnit == MapUnit::MapAppFont ||
1267 eOutUnit == MapUnit::MapRelative )
1269 OSL_FAIL( "invalid parameters" );
1270 return nValue;
1273 long nDecDigits = nDigits;
1274 FieldUnit eFieldUnit = ImplMap2FieldUnit( eOutUnit, nDecDigits );
1276 if ( nDecDigits < 0 )
1278 nValue *= ImplPower10(-nDecDigits);
1280 else
1282 nValue /= ImplPower10(nDecDigits);
1285 if ( eFieldUnit != eInUnit )
1287 sal_Int64 nDiv = aImplFactor[eInUnit][eFieldUnit];
1288 sal_Int64 nMult = aImplFactor[eFieldUnit][eInUnit];
1290 SAL_WARN_IF( nMult <= 0, "vcl", "illegal *" );
1291 SAL_WARN_IF( nDiv <= 0, "vcl", "illegal /" );
1293 if( nMult != 1 && nMult > 0 )
1294 nValue *= nMult;
1295 if( nDiv != 1 && nDiv > 0 )
1297 nValue += (nValue < 0) ? (-nDiv/2) : (nDiv/2);
1298 nValue /= nDiv;
1301 return nValue;
1304 static bool ImplMetricGetValue( const OUString& rStr, double& rValue, sal_Int64 nBaseValue,
1305 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper, FieldUnit eUnit )
1307 // Get value
1308 sal_Int64 nValue;
1309 if ( !ImplNumericGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper ) )
1310 return false;
1312 // Determine unit
1313 FieldUnit eEntryUnit = ImplMetricGetUnit( rStr );
1315 // Recalculate unit
1316 // caution: conversion to double loses precision
1317 rValue = MetricField::ConvertDoubleValue( (double)nValue, nBaseValue, nDecDigits, eEntryUnit, eUnit );
1319 return true;
1322 bool MetricFormatter::ImplMetricReformat( const OUString& rStr, double& rValue, OUString& rOutStr )
1324 if ( !ImplMetricGetValue( rStr, rValue, mnBaseValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit ) )
1325 return true;
1326 else
1328 double nTempVal = rValue;
1329 // caution: precision loss in double cast
1330 if ( nTempVal > GetMax() )
1331 nTempVal = (double)GetMax();
1332 else if ( nTempVal < GetMin())
1333 nTempVal = (double)GetMin();
1335 rOutStr = CreateFieldText( (sal_Int64)nTempVal );
1336 return true;
1340 inline void MetricFormatter::ImplInit()
1342 mnBaseValue = 0;
1343 meUnit = MetricField::GetDefaultUnit();
1344 mnType = FORMAT_METRIC;
1347 MetricFormatter::MetricFormatter()
1349 ImplInit();
1352 MetricFormatter::~MetricFormatter()
1356 void MetricFormatter::SetUnit( FieldUnit eNewUnit )
1358 if ( eNewUnit == FUNIT_100TH_MM )
1360 SetDecimalDigits( GetDecimalDigits() + 2 );
1361 meUnit = FUNIT_MM;
1363 else
1364 meUnit = eNewUnit;
1365 ReformatAll();
1368 void MetricFormatter::SetCustomUnitText( const OUString& rStr )
1370 maCustomUnitText = rStr;
1371 ReformatAll();
1374 void MetricFormatter::SetValue( sal_Int64 nNewValue, FieldUnit eInUnit )
1376 SetUserValue( nNewValue, eInUnit );
1377 mnFieldValue = mnLastValue;
1380 OUString MetricFormatter::CreateFieldText( sal_Int64 nValue ) const
1382 //whether percent is separated from its number is locale
1383 //specific, pawn it off to icu to decide
1384 if (meUnit == FUNIT_PERCENT)
1386 double dValue = nValue;
1387 dValue /= ImplPower10(GetDecimalDigits());
1388 return unicode::formatPercent(dValue, Application::GetSettings().GetUILanguageTag());
1391 OUString aStr = NumericFormatter::CreateFieldText( nValue );
1393 if( meUnit == FUNIT_CUSTOM )
1394 aStr += maCustomUnitText;
1395 else
1397 if (meUnit != FUNIT_NONE && meUnit != FUNIT_DEGREE)
1398 aStr += " ";
1399 assert(meUnit != FUNIT_PERCENT);
1400 aStr += ImplMetricToString( meUnit );
1402 return aStr;
1405 void MetricFormatter::SetUserValue( sal_Int64 nNewValue, FieldUnit eInUnit )
1407 // convert to previously configured units
1408 nNewValue = MetricField::ConvertValue( nNewValue, mnBaseValue, GetDecimalDigits(), eInUnit, meUnit );
1409 NumericFormatter::SetUserValue( nNewValue );
1412 sal_Int64 MetricFormatter::GetValue( FieldUnit eOutUnit ) const
1414 if ( !GetField() )
1415 return 0;
1417 double nTempValue;
1418 // caution: precision loss in double cast
1419 if ( !ImplMetricGetValue( GetField()->GetText(), nTempValue, mnBaseValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit ) )
1420 nTempValue = (double)mnLastValue;
1422 // caution: precision loss in double cast
1423 if ( nTempValue > mnMax )
1424 nTempValue = (double)mnMax;
1425 else if ( nTempValue < mnMin )
1426 nTempValue = (double)mnMin;
1428 // convert to requested units
1429 return MetricField::ConvertValue( (sal_Int64)nTempValue, mnBaseValue, GetDecimalDigits(), meUnit, eOutUnit );
1432 void MetricFormatter::SetValue( sal_Int64 nValue )
1434 // Implementation not inline, because it is a virtual Function
1435 SetValue( nValue, FUNIT_NONE );
1438 sal_Int64 MetricFormatter::GetValue() const
1440 // Implementation not inline, because it is a virtual Function
1441 return GetValue( FUNIT_NONE );
1444 void MetricFormatter::SetMin( sal_Int64 nNewMin, FieldUnit eInUnit )
1446 // convert to requested units
1447 NumericFormatter::SetMin( MetricField::ConvertValue( nNewMin, mnBaseValue, GetDecimalDigits(),
1448 eInUnit, meUnit ) );
1451 sal_Int64 MetricFormatter::GetMin( FieldUnit eOutUnit ) const
1453 // convert to requested units
1454 return MetricField::ConvertValue( NumericFormatter::GetMin(), mnBaseValue,
1455 GetDecimalDigits(), meUnit, eOutUnit );
1458 void MetricFormatter::SetMax( sal_Int64 nNewMax, FieldUnit eInUnit )
1460 // convert to requested units
1461 NumericFormatter::SetMax( MetricField::ConvertValue( nNewMax, mnBaseValue, GetDecimalDigits(),
1462 eInUnit, meUnit ) );
1465 sal_Int64 MetricFormatter::GetMax( FieldUnit eOutUnit ) const
1467 // convert to requested units
1468 return MetricField::ConvertValue( NumericFormatter::GetMax(), mnBaseValue,
1469 GetDecimalDigits(), meUnit, eOutUnit );
1472 void MetricFormatter::SetBaseValue( sal_Int64 nNewBase, FieldUnit eInUnit )
1474 mnBaseValue = MetricField::ConvertValue( nNewBase, mnBaseValue, GetDecimalDigits(),
1475 eInUnit, meUnit );
1478 sal_Int64 MetricFormatter::GetBaseValue() const
1480 // convert to requested units
1481 return MetricField::ConvertValue( mnBaseValue, mnBaseValue, GetDecimalDigits(),
1482 meUnit, FUNIT_NONE );
1485 void MetricFormatter::Reformat()
1487 if ( !GetField() )
1488 return;
1490 OUString aText = GetField()->GetText();
1491 if ( meUnit == FUNIT_CUSTOM )
1492 maCurUnitText = ImplMetricGetUnitText( aText );
1494 OUString aStr;
1495 // caution: precision loss in double cast
1496 double nTemp = (double)mnLastValue;
1497 bool bOK = ImplMetricReformat( aText, nTemp, aStr );
1498 mnLastValue = (sal_Int64)nTemp;
1500 if ( !bOK )
1501 return;
1503 if ( !aStr.isEmpty() )
1505 ImplSetText( aStr );
1506 if ( meUnit == FUNIT_CUSTOM )
1507 CustomConvert();
1509 else
1510 SetValue( mnLastValue );
1511 maCurUnitText.clear();
1514 sal_Int64 MetricFormatter::GetCorrectedValue( FieldUnit eOutUnit ) const
1516 // convert to requested units
1517 return MetricField::ConvertValue( 0/*nCorrectedValue*/, mnBaseValue, GetDecimalDigits(),
1518 meUnit, eOutUnit );
1521 MetricField::MetricField( vcl::Window* pParent, WinBits nWinStyle ) :
1522 SpinField( pParent, nWinStyle )
1524 SetField( this );
1525 Reformat();
1528 void MetricField::dispose()
1530 MetricFormatter::SetField( nullptr );
1531 SpinField::dispose();
1534 Size MetricField::CalcMinimumSize() const
1536 return calcMinimumSize(*this, *this);
1539 bool MetricField::set_property(const OString &rKey, const OString &rValue)
1541 if (rKey == "digits")
1542 SetDecimalDigits(rValue.toInt32());
1543 else if (rKey == "spin-size")
1544 SetSpinSize(rValue.toInt32());
1545 else
1546 return SpinField::set_property(rKey, rValue);
1547 return true;
1550 void MetricField::SetUnit( FieldUnit nNewUnit )
1552 sal_Int64 nRawMax = GetMax( nNewUnit );
1553 sal_Int64 nMax = Denormalize( nRawMax );
1554 sal_Int64 nMin = Denormalize( GetMin( nNewUnit ) );
1555 sal_Int64 nFirst = Denormalize( GetFirst( nNewUnit ) );
1556 sal_Int64 nLast = Denormalize( GetLast( nNewUnit ) );
1558 MetricFormatter::SetUnit( nNewUnit );
1560 SetMax( Normalize( nMax ), nNewUnit );
1561 SetMin( Normalize( nMin ), nNewUnit );
1562 SetFirst( Normalize( nFirst ), nNewUnit );
1563 SetLast( Normalize( nLast ), nNewUnit );
1566 void MetricField::SetFirst( sal_Int64 nNewFirst, FieldUnit eInUnit )
1568 // convert
1569 nNewFirst = MetricField::ConvertValue( nNewFirst, mnBaseValue, GetDecimalDigits(),
1570 eInUnit, meUnit );
1571 mnFirst = nNewFirst;
1574 sal_Int64 MetricField::GetFirst( FieldUnit eOutUnit ) const
1576 // convert
1577 return MetricField::ConvertValue( mnFirst, mnBaseValue, GetDecimalDigits(),
1578 meUnit, eOutUnit );
1581 void MetricField::SetLast( sal_Int64 nNewLast, FieldUnit eInUnit )
1583 // convert
1584 nNewLast = MetricField::ConvertValue( nNewLast, mnBaseValue, GetDecimalDigits(),
1585 eInUnit, meUnit );
1586 mnLast = nNewLast;
1589 sal_Int64 MetricField::GetLast( FieldUnit eOutUnit ) const
1591 // conver
1592 return MetricField::ConvertValue( mnLast, mnBaseValue, GetDecimalDigits(),
1593 meUnit, eOutUnit );
1596 bool MetricField::PreNotify( NotifyEvent& rNEvt )
1598 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1600 if ( ImplMetricProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1601 return true;
1604 return SpinField::PreNotify( rNEvt );
1607 bool MetricField::EventNotify( NotifyEvent& rNEvt )
1609 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
1610 MarkToBeReformatted( false );
1611 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
1613 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1614 Reformat();
1617 return SpinField::EventNotify( rNEvt );
1620 void MetricField::DataChanged( const DataChangedEvent& rDCEvt )
1622 SpinField::DataChanged( rDCEvt );
1624 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
1626 OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1627 OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1628 if ( IsDefaultLocale() )
1629 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
1630 OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1631 OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1632 ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1633 ReformatAll();
1637 void MetricField::Modify()
1639 MarkToBeReformatted( true );
1640 SpinField::Modify();
1643 void MetricField::Up()
1645 FieldUp();
1646 SpinField::Up();
1649 void MetricField::Down()
1651 FieldDown();
1652 SpinField::Down();
1655 void MetricField::First()
1657 FieldFirst();
1658 SpinField::First();
1661 void MetricField::Last()
1663 FieldLast();
1664 SpinField::Last();
1667 void MetricField::CustomConvert()
1669 maCustomConvertLink.Call( *this );
1672 MetricBox::MetricBox( vcl::Window* pParent, WinBits nWinStyle ) :
1673 ComboBox( pParent, nWinStyle )
1675 SetField( this );
1676 Reformat();
1679 void MetricBox::dispose()
1681 MetricFormatter::SetField(nullptr);
1682 ComboBox::dispose();
1685 Size MetricBox::CalcMinimumSize() const
1687 Size aRet(calcMinimumSize(*this, *this));
1689 if (IsDropDownBox())
1691 Size aComboSugg(ComboBox::CalcMinimumSize());
1692 aRet.Width() = std::max(aRet.Width(), aComboSugg.Width());
1693 aRet.Height() = std::max(aRet.Height(), aComboSugg.Height());
1696 return aRet;
1699 bool MetricBox::PreNotify( NotifyEvent& rNEvt )
1701 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1703 if ( ImplMetricProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1704 return true;
1707 return ComboBox::PreNotify( rNEvt );
1710 bool MetricBox::EventNotify( NotifyEvent& rNEvt )
1712 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
1713 MarkToBeReformatted( false );
1714 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
1716 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1717 Reformat();
1720 return ComboBox::EventNotify( rNEvt );
1723 void MetricBox::DataChanged( const DataChangedEvent& rDCEvt )
1725 ComboBox::DataChanged( rDCEvt );
1727 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
1729 OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1730 OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1731 if ( IsDefaultLocale() )
1732 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
1733 OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1734 OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1735 ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1736 ReformatAll();
1740 void MetricBox::Modify()
1742 MarkToBeReformatted( true );
1743 ComboBox::Modify();
1746 void MetricBox::ReformatAll()
1748 double nValue;
1749 OUString aStr;
1750 SetUpdateMode( false );
1751 sal_Int32 nEntryCount = GetEntryCount();
1752 for ( sal_Int32 i=0; i < nEntryCount; i++ )
1754 ImplMetricReformat( GetEntry( i ), nValue, aStr );
1755 RemoveEntryAt(i);
1756 InsertEntry( aStr, i );
1758 MetricFormatter::Reformat();
1759 SetUpdateMode( true );
1762 void MetricBox::CustomConvert()
1764 maCustomConvertLink.Call( *this );
1767 void MetricBox::InsertValue( sal_Int64 nValue, FieldUnit eInUnit, sal_Int32 nPos )
1769 // convert to previously configured units
1770 nValue = MetricField::ConvertValue( nValue, mnBaseValue, GetDecimalDigits(),
1771 eInUnit, meUnit );
1772 ComboBox::InsertEntry( CreateFieldText( nValue ), nPos );
1775 sal_Int64 MetricBox::GetValue( sal_Int32 nPos ) const
1777 double nValue = 0;
1778 ImplMetricGetValue( ComboBox::GetEntry( nPos ), nValue, mnBaseValue,
1779 GetDecimalDigits(), ImplGetLocaleDataWrapper(), meUnit );
1781 // convert to previously configured units
1782 sal_Int64 nRetValue = MetricField::ConvertValue( (sal_Int64)nValue, mnBaseValue, GetDecimalDigits(),
1783 meUnit, FUNIT_NONE );
1785 return nRetValue;
1788 sal_Int32 MetricBox::GetValuePos( sal_Int64 nValue, FieldUnit eInUnit ) const
1790 // convert to previously configured units
1791 nValue = MetricField::ConvertValue( nValue, mnBaseValue, GetDecimalDigits(),
1792 eInUnit, meUnit );
1793 return ComboBox::GetEntryPos( CreateFieldText( nValue ) );
1796 sal_Int64 MetricBox::GetValue( FieldUnit eOutUnit ) const
1798 // Implementation not inline, because it is a virtual Function
1799 return MetricFormatter::GetValue( eOutUnit );
1802 sal_Int64 MetricBox::GetValue() const
1804 // Implementation not inline, because it is a virtual Function
1805 return GetValue( FUNIT_NONE );
1808 static bool ImplCurrencyProcessKeyInput( Edit* pEdit, const KeyEvent& rKEvt,
1809 bool, bool bUseThousandSep, const LocaleDataWrapper& rWrapper )
1811 // no strict format set; therefore allow all characters
1812 return ImplNumericProcessKeyInput( pEdit, rKEvt, false, bUseThousandSep, rWrapper );
1815 inline bool ImplCurrencyGetValue( const OUString& rStr, sal_Int64& rValue,
1816 sal_uInt16 nDecDigits, const LocaleDataWrapper& rWrapper )
1818 // fetch number
1819 return ImplNumericGetValue( rStr, rValue, nDecDigits, rWrapper, true );
1822 bool CurrencyFormatter::ImplCurrencyReformat( const OUString& rStr, OUString& rOutStr )
1824 sal_Int64 nValue;
1825 if ( !ImplNumericGetValue( rStr, nValue, GetDecimalDigits(), ImplGetLocaleDataWrapper(), true ) )
1826 return true;
1827 else
1829 sal_Int64 nTempVal = nValue;
1830 if ( nTempVal > GetMax() )
1831 nTempVal = GetMax();
1832 else if ( nTempVal < GetMin())
1833 nTempVal = GetMin();
1835 rOutStr = CreateFieldText( nTempVal );
1836 return true;
1840 CurrencyFormatter::CurrencyFormatter()
1842 mnType = FORMAT_CURRENCY;
1845 CurrencyFormatter::~CurrencyFormatter()
1849 void CurrencyFormatter::SetValue( sal_Int64 nNewValue )
1851 SetUserValue( nNewValue );
1852 mnFieldValue = mnLastValue;
1853 SetEmptyFieldValueData( false );
1856 OUString CurrencyFormatter::CreateFieldText( sal_Int64 nValue ) const
1858 return ImplGetLocaleDataWrapper().getCurr( nValue, GetDecimalDigits(),
1859 ImplGetLocaleDataWrapper().getCurrSymbol(),
1860 IsUseThousandSep() );
1863 sal_Int64 CurrencyFormatter::GetValue() const
1865 if ( !GetField() )
1866 return 0;
1868 sal_Int64 nTempValue;
1869 if ( ImplCurrencyGetValue( GetField()->GetText(), nTempValue, GetDecimalDigits(), ImplGetLocaleDataWrapper() ) )
1871 return ClipAgainstMinMax(nTempValue);
1873 else
1874 return mnLastValue;
1877 void CurrencyFormatter::Reformat()
1879 if ( !GetField() )
1880 return;
1882 OUString aStr;
1883 bool bOK = ImplCurrencyReformat( GetField()->GetText(), aStr );
1884 if ( !bOK )
1885 return;
1887 if ( !aStr.isEmpty() )
1889 ImplSetText( aStr );
1890 sal_Int64 nTemp = mnLastValue;
1891 ImplCurrencyGetValue( aStr, nTemp, GetDecimalDigits(), ImplGetLocaleDataWrapper() );
1892 mnLastValue = nTemp;
1894 else
1895 SetValue( mnLastValue );
1898 CurrencyField::CurrencyField( vcl::Window* pParent, WinBits nWinStyle ) :
1899 SpinField( pParent, nWinStyle )
1901 SetField( this );
1902 Reformat();
1905 void CurrencyField::dispose()
1907 CurrencyFormatter::SetField( nullptr );
1908 SpinField::dispose();
1911 bool CurrencyField::PreNotify( NotifyEvent& rNEvt )
1913 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1915 if ( ImplCurrencyProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
1916 return true;
1919 return SpinField::PreNotify( rNEvt );
1922 bool CurrencyField::EventNotify( NotifyEvent& rNEvt )
1924 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
1925 MarkToBeReformatted( false );
1926 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
1928 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
1929 Reformat();
1932 return SpinField::EventNotify( rNEvt );
1935 void CurrencyField::DataChanged( const DataChangedEvent& rDCEvt )
1937 SpinField::DataChanged( rDCEvt );
1939 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
1941 OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1942 OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1943 if ( IsDefaultLocale() )
1944 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
1945 OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
1946 OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
1947 ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
1948 ReformatAll();
1952 void CurrencyField::Modify()
1954 MarkToBeReformatted( true );
1955 SpinField::Modify();
1958 void CurrencyField::Up()
1960 FieldUp();
1961 SpinField::Up();
1964 void CurrencyField::Down()
1966 FieldDown();
1967 SpinField::Down();
1970 void CurrencyField::First()
1972 FieldFirst();
1973 SpinField::First();
1976 void CurrencyField::Last()
1978 FieldLast();
1979 SpinField::Last();
1982 CurrencyBox::CurrencyBox( vcl::Window* pParent, WinBits nWinStyle ) :
1983 ComboBox( pParent, nWinStyle )
1985 SetField( this );
1986 Reformat();
1989 void CurrencyBox::dispose()
1991 CurrencyFormatter::SetField( nullptr );
1992 ComboBox::dispose();
1995 bool CurrencyBox::PreNotify( NotifyEvent& rNEvt )
1997 if ( (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT) && !rNEvt.GetKeyEvent()->GetKeyCode().IsMod2() )
1999 if ( ImplCurrencyProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), ImplGetLocaleDataWrapper() ) )
2000 return true;
2003 return ComboBox::PreNotify( rNEvt );
2006 bool CurrencyBox::EventNotify( NotifyEvent& rNEvt )
2008 if ( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
2009 MarkToBeReformatted( false );
2010 else if ( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
2012 if ( MustBeReformatted() && (!GetText().isEmpty() || !IsEmptyFieldValueEnabled()) )
2013 Reformat();
2016 return ComboBox::EventNotify( rNEvt );
2019 void CurrencyBox::DataChanged( const DataChangedEvent& rDCEvt )
2021 ComboBox::DataChanged( rDCEvt );
2023 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::LOCALE) )
2025 OUString sOldDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
2026 OUString sOldThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
2027 if ( IsDefaultLocale() )
2028 ImplGetLocaleDataWrapper().setLanguageTag( GetSettings().GetLanguageTag() );
2029 OUString sNewDecSep = ImplGetLocaleDataWrapper().getNumDecimalSep();
2030 OUString sNewThSep = ImplGetLocaleDataWrapper().getNumThousandSep();
2031 ImplUpdateSeparators( sOldDecSep, sNewDecSep, sOldThSep, sNewThSep, this );
2032 ReformatAll();
2036 void CurrencyBox::Modify()
2038 MarkToBeReformatted( true );
2039 ComboBox::Modify();
2042 void CurrencyBox::ReformatAll()
2044 OUString aStr;
2045 SetUpdateMode( false );
2046 sal_Int32 nEntryCount = GetEntryCount();
2047 for ( sal_Int32 i=0; i < nEntryCount; i++ )
2049 ImplCurrencyReformat( GetEntry( i ), aStr );
2050 RemoveEntryAt(i);
2051 InsertEntry( aStr, i );
2053 CurrencyFormatter::Reformat();
2054 SetUpdateMode( true );
2057 sal_Int64 CurrencyBox::GetValue() const
2059 // Implementation not inline, because it is a virtual Function
2060 return CurrencyFormatter::GetValue();
2063 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */