bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / control / longcurr.cxx
blob6505df620c10fe190505ee87b5a201bef695ce55
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 <string_view>
24 #include <comphelper/string.hxx>
25 #include <tools/bigint.hxx>
26 #include <sal/log.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/longcurr.hxx>
31 #include <unotools/localedatawrapper.hxx>
33 using namespace ::comphelper;
35 namespace
38 BigInt ImplPower10( sal_uInt16 n )
40 sal_uInt16 i;
41 BigInt nValue = 1;
43 for ( i=0; i < n; i++ )
44 nValue *= 10;
46 return nValue;
49 OUString ImplGetCurr( const LocaleDataWrapper& rLocaleDataWrapper, const BigInt &rNumber, sal_uInt16 nDigits, const OUString& rCurrSymbol, bool bShowThousandSep )
51 SAL_WARN_IF( nDigits >= 10, "vcl", "LongCurrency may only have 9 decimal places" );
53 if ( rNumber.IsZero() || static_cast<long>(rNumber) )
54 return rLocaleDataWrapper.getCurr( static_cast<long>(rNumber), nDigits, rCurrSymbol, bShowThousandSep );
56 BigInt aTmp( ImplPower10( nDigits ) );
57 BigInt aInteger( rNumber );
58 aInteger.Abs();
59 aInteger /= aTmp;
60 BigInt aFraction( rNumber );
61 aFraction.Abs();
62 aFraction %= aTmp;
63 if ( !aInteger.IsZero() )
65 aFraction += aTmp;
66 aTmp = 1000000000;
68 if ( rNumber.IsNeg() )
69 aFraction *= -1;
71 OUStringBuffer aTemplate = rLocaleDataWrapper.getCurr( static_cast<long>(aFraction), nDigits, rCurrSymbol, bShowThousandSep );
72 while( !aInteger.IsZero() )
74 aFraction = aInteger;
75 aFraction %= aTmp;
76 aInteger /= aTmp;
77 if( !aInteger.IsZero() )
78 aFraction += aTmp;
80 OUString aFractionStr = rLocaleDataWrapper.getNum( static_cast<long>(aFraction), 0 );
82 sal_Int32 nSPos = aTemplate.indexOf( '1' );
83 if (nSPos == -1)
84 break;
85 if ( aFractionStr.getLength() == 1 )
86 aTemplate[ nSPos ] = aFractionStr[0];
87 else
89 aTemplate.remove( nSPos, 1 );
90 aTemplate.insert( nSPos, aFractionStr );
94 return aTemplate.makeStringAndClear();
97 bool ImplCurrencyGetValue( const OUString& rStr, BigInt& rValue,
98 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper )
100 OUString aStr = rStr;
101 OUStringBuffer aStr1;
102 OUStringBuffer aStr2;
103 sal_Int32 nDecPos;
104 bool bNegative = false;
106 // On empty string
107 if ( rStr.isEmpty() )
108 return false;
110 // Trim leading and trailing spaces
111 aStr = string::strip(aStr, ' ');
113 // Find decimal sign's position
114 nDecPos = aStr.indexOf( rLocaleDataWrapper.getNumDecimalSep() );
115 if (nDecPos < 0 && !rLocaleDataWrapper.getNumDecimalSepAlt().isEmpty())
116 nDecPos = aStr.indexOf( rLocaleDataWrapper.getNumDecimalSepAlt() );
118 if ( nDecPos != -1 )
120 aStr1 = aStr.copy( 0, nDecPos );
121 aStr2.append(std::u16string_view(aStr).substr(nDecPos+1));
123 else
124 aStr1 = aStr;
126 // Negative?
127 if ( (aStr[ 0 ] == '(') && (aStr[ aStr.getLength()-1 ] == ')') )
128 bNegative = true;
129 if ( !bNegative )
131 for (sal_Int32 i=0; i < aStr.getLength(); i++ )
133 if ( (aStr[ i ] >= '0') && (aStr[ i ] <= '9') )
134 break;
135 else if ( aStr[ i ] == '-' )
137 bNegative = true;
138 break;
142 if ( !bNegative && !aStr.isEmpty() )
144 sal_uInt16 nFormat = rLocaleDataWrapper.getCurrNegativeFormat();
145 if ( (nFormat == 3) || (nFormat == 6) ||
146 (nFormat == 7) || (nFormat == 10) )
148 for (sal_Int32 i = aStr.getLength()-1; i > 0; i++ )
150 if ( (aStr[ i ] >= '0') && (aStr[ i ] <= '9') )
151 break;
152 else if ( aStr[ i ] == '-' )
154 bNegative = true;
155 break;
161 // delete unwanted characters
162 for (sal_Int32 i=0; i < aStr1.getLength(); )
164 if ( (aStr1[ i ] >= '0') && (aStr1[ i ] <= '9') )
165 i++;
166 else
167 aStr1.remove( i, 1 );
169 for (sal_Int32 i=0; i < aStr2.getLength(); )
171 if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
172 ++i;
173 else
174 aStr2.remove(i, 1);
177 if ( aStr1.isEmpty() && aStr2.isEmpty())
178 return false;
180 if ( aStr1.isEmpty() )
181 aStr1 = "0";
182 if ( bNegative )
183 aStr1.insert( 0, '-');
185 // Cut down decimal part and round while doing so
186 bool bRound = false;
187 if (aStr2.getLength() > nDecDigits)
189 if (aStr2[nDecDigits] >= '5')
190 bRound = true;
191 string::truncateToLength(aStr2, nDecDigits);
193 if (aStr2.getLength() < nDecDigits)
194 string::padToLength(aStr2, nDecDigits, '0');
196 aStr1.append(aStr2);
197 aStr = aStr1.makeStringAndClear();
199 // check range
200 BigInt nValue( aStr );
201 if ( bRound )
203 if ( !bNegative )
204 nValue+=1;
205 else
206 nValue-=1;
209 rValue = nValue;
211 return true;
214 } // namespace
216 static bool ImplLongCurrencyGetValue( const OUString& rStr, BigInt& rValue,
217 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper )
219 return ImplCurrencyGetValue( rStr, rValue, nDecDigits, rLocaleDataWrapper );
222 bool ImplLongCurrencyReformat( const OUString& rStr, BigInt const & nMin, BigInt const & nMax,
223 sal_uInt16 nDecDigits,
224 const LocaleDataWrapper& rLocaleDataWrapper, OUString& rOutStr,
225 LongCurrencyFormatter const & rFormatter )
227 BigInt nValue;
228 if ( !ImplCurrencyGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper ) )
229 return true;
230 else
232 BigInt nTempVal = nValue;
233 if ( nTempVal > nMax )
234 nTempVal = nMax;
235 else if ( nTempVal < nMin )
236 nTempVal = nMin;
238 rOutStr = ImplGetCurr( rLocaleDataWrapper, nTempVal, nDecDigits, rFormatter.GetCurrencySymbol(), rFormatter.IsUseThousandSep() );
239 return true;
243 void LongCurrencyFormatter::ImpInit()
245 mnLastValue = 0;
246 mnMin = 0;
247 mnMax = 0x7FFFFFFF;
248 mnMax *= 0x7FFFFFFF;
249 mnDecimalDigits = 0;
250 mbThousandSep = true;
251 SetDecimalDigits( 0 );
254 LongCurrencyFormatter::LongCurrencyFormatter(Edit* pEdit)
255 : FormatterBase(pEdit)
257 ImpInit();
260 LongCurrencyFormatter::~LongCurrencyFormatter()
264 void LongCurrencyFormatter::SetCurrencySymbol( const OUString& rStr )
266 maCurrencySymbol= rStr;
267 ReformatAll();
270 OUString const & LongCurrencyFormatter::GetCurrencySymbol() const
272 return !maCurrencySymbol.isEmpty() ? maCurrencySymbol : GetLocaleDataWrapper().getCurrSymbol();
275 void LongCurrencyFormatter::SetValue(const BigInt& rNewValue)
277 SetUserValue(rNewValue);
278 SetEmptyFieldValueData( false );
281 void LongCurrencyFormatter::SetUserValue( BigInt nNewValue )
283 if ( nNewValue > mnMax )
284 nNewValue = mnMax;
285 else if ( nNewValue < mnMin )
286 nNewValue = mnMin;
287 mnLastValue = nNewValue;
289 if ( !GetField() )
290 return;
292 OUString aStr = ImplGetCurr( GetLocaleDataWrapper(), nNewValue, GetDecimalDigits(), GetCurrencySymbol(), IsUseThousandSep() );
293 if ( GetField()->HasFocus() )
295 Selection aSelection = GetField()->GetSelection();
296 GetField()->SetText( aStr );
297 GetField()->SetSelection( aSelection );
299 else
300 GetField()->SetText( aStr );
301 MarkToBeReformatted( false );
304 BigInt LongCurrencyFormatter::GetValue() const
306 if ( !GetField() )
307 return 0;
309 BigInt nTempValue;
310 if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue, GetDecimalDigits(), GetLocaleDataWrapper() ) )
312 if ( nTempValue > mnMax )
313 nTempValue = mnMax;
314 else if ( nTempValue < mnMin )
315 nTempValue = mnMin;
316 return nTempValue;
318 else
319 return mnLastValue;
322 void LongCurrencyFormatter::Reformat()
324 if ( !GetField() )
325 return;
327 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
328 return;
330 OUString aStr;
331 bool bOK = ImplLongCurrencyReformat( GetField()->GetText(), mnMin, mnMax,
332 GetDecimalDigits(), GetLocaleDataWrapper(), aStr, *this );
333 if ( !bOK )
334 return;
336 if ( !aStr.isEmpty() )
338 GetField()->SetText( aStr );
339 MarkToBeReformatted( false );
340 ImplLongCurrencyGetValue( aStr, mnLastValue, GetDecimalDigits(), GetLocaleDataWrapper() );
342 else
343 SetValue( mnLastValue );
346 void LongCurrencyFormatter::ReformatAll()
348 Reformat();
351 void LongCurrencyFormatter::SetMin(const BigInt& rNewMin)
353 mnMin = rNewMin;
354 ReformatAll();
357 void LongCurrencyFormatter::SetMax(const BigInt& rNewMax)
359 mnMax = rNewMax;
360 ReformatAll();
363 void LongCurrencyFormatter::SetUseThousandSep( bool b )
365 mbThousandSep = b;
366 ReformatAll();
369 void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits )
371 if ( nDigits > 9 )
372 nDigits = 9;
374 mnDecimalDigits = nDigits;
375 ReformatAll();
379 void ImplNewLongCurrencyFieldValue(LongCurrencyField* pField, const BigInt& rNewValue)
381 Selection aSelect = pField->GetSelection();
382 aSelect.Justify();
383 OUString aText = pField->GetText();
384 bool bLastSelected = aSelect.Max() == aText.getLength();
386 BigInt nOldLastValue = pField->mnLastValue;
387 pField->SetUserValue(rNewValue);
388 pField->mnLastValue = nOldLastValue;
390 if ( bLastSelected )
392 if ( !aSelect.Len() )
393 aSelect.Min() = SELECTION_MAX;
394 aSelect.Max() = SELECTION_MAX;
396 pField->SetSelection( aSelect );
397 pField->SetModifyFlag();
398 pField->Modify();
401 LongCurrencyField::LongCurrencyField(vcl::Window* pParent, WinBits nWinStyle)
402 : SpinField( pParent, nWinStyle )
403 , LongCurrencyFormatter(this)
405 mnSpinSize = 1;
406 mnFirst = mnMin;
407 mnLast = mnMax;
409 Reformat();
412 bool LongCurrencyField::EventNotify( NotifyEvent& rNEvt )
414 if( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
416 MarkToBeReformatted( false );
418 else if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
420 if ( MustBeReformatted() )
422 Reformat();
423 SpinField::Modify();
426 return SpinField::EventNotify( rNEvt );
429 void LongCurrencyField::Modify()
431 MarkToBeReformatted( true );
432 SpinField::Modify();
435 void LongCurrencyField::Up()
437 BigInt nValue = GetValue();
438 nValue += mnSpinSize;
439 if ( nValue > mnMax )
440 nValue = mnMax;
442 ImplNewLongCurrencyFieldValue( this, nValue );
443 SpinField::Up();
446 void LongCurrencyField::Down()
448 BigInt nValue = GetValue();
449 nValue -= mnSpinSize;
450 if ( nValue < mnMin )
451 nValue = mnMin;
453 ImplNewLongCurrencyFieldValue( this, nValue );
454 SpinField::Down();
457 void LongCurrencyField::First()
459 ImplNewLongCurrencyFieldValue( this, mnFirst );
460 SpinField::First();
463 void LongCurrencyField::Last()
465 ImplNewLongCurrencyFieldValue( this, mnLast );
466 SpinField::Last();
469 LongCurrencyBox::LongCurrencyBox(vcl::Window* pParent, WinBits nWinStyle)
470 : ComboBox(pParent, nWinStyle)
471 , LongCurrencyFormatter(this)
473 Reformat();
476 bool LongCurrencyBox::EventNotify( NotifyEvent& rNEvt )
478 if( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
480 MarkToBeReformatted( false );
482 else if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
484 if ( MustBeReformatted() )
486 Reformat();
487 ComboBox::Modify();
490 return ComboBox::EventNotify( rNEvt );
493 void LongCurrencyBox::Modify()
495 MarkToBeReformatted( true );
496 ComboBox::Modify();
499 void LongCurrencyBox::ReformatAll()
501 OUString aStr;
502 SetUpdateMode( false );
503 const sal_Int32 nEntryCount = GetEntryCount();
504 for ( sal_Int32 i=0; i < nEntryCount; ++i )
506 ImplLongCurrencyReformat( GetEntry( i ), mnMin, mnMax,
507 GetDecimalDigits(), GetLocaleDataWrapper(),
508 aStr, *this );
509 RemoveEntryAt(i);
510 InsertEntry( aStr, i );
512 LongCurrencyFormatter::Reformat();
513 SetUpdateMode( true );
516 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */