1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
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
;
38 BigInt
ImplPower10( sal_uInt16 n
)
43 for ( i
=0; i
< n
; i
++ )
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
);
60 BigInt
aFraction( rNumber
);
63 if ( !aInteger
.IsZero() )
68 if ( rNumber
.IsNeg() )
71 OUStringBuffer aTemplate
= rLocaleDataWrapper
.getCurr( static_cast<long>(aFraction
), nDigits
, rCurrSymbol
, bShowThousandSep
);
72 while( !aInteger
.IsZero() )
77 if( !aInteger
.IsZero() )
80 OUString aFractionStr
= rLocaleDataWrapper
.getNum( static_cast<long>(aFraction
), 0 );
82 sal_Int32 nSPos
= aTemplate
.indexOf( '1' );
85 if ( aFractionStr
.getLength() == 1 )
86 aTemplate
[ nSPos
] = aFractionStr
[0];
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
;
104 bool bNegative
= false;
107 if ( rStr
.isEmpty() )
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() );
120 aStr1
= aStr
.copy( 0, nDecPos
);
121 aStr2
.append(std::u16string_view(aStr
).substr(nDecPos
+1));
127 if ( (aStr
[ 0 ] == '(') && (aStr
[ aStr
.getLength()-1 ] == ')') )
131 for (sal_Int32 i
=0; i
< aStr
.getLength(); i
++ )
133 if ( (aStr
[ i
] >= '0') && (aStr
[ i
] <= '9') )
135 else if ( aStr
[ i
] == '-' )
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') )
152 else if ( aStr
[ i
] == '-' )
161 // delete unwanted characters
162 for (sal_Int32 i
=0; i
< aStr1
.getLength(); )
164 if ( (aStr1
[ i
] >= '0') && (aStr1
[ i
] <= '9') )
167 aStr1
.remove( i
, 1 );
169 for (sal_Int32 i
=0; i
< aStr2
.getLength(); )
171 if ((aStr2
[i
] >= '0') && (aStr2
[i
] <= '9'))
177 if ( aStr1
.isEmpty() && aStr2
.isEmpty())
180 if ( aStr1
.isEmpty() )
183 aStr1
.insert( 0, '-');
185 // Cut down decimal part and round while doing so
187 if (aStr2
.getLength() > nDecDigits
)
189 if (aStr2
[nDecDigits
] >= '5')
191 string::truncateToLength(aStr2
, nDecDigits
);
193 if (aStr2
.getLength() < nDecDigits
)
194 string::padToLength(aStr2
, nDecDigits
, '0');
197 aStr
= aStr1
.makeStringAndClear();
200 BigInt
nValue( aStr
);
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
)
228 if ( !ImplCurrencyGetValue( rStr
, nValue
, nDecDigits
, rLocaleDataWrapper
) )
232 BigInt nTempVal
= nValue
;
233 if ( nTempVal
> nMax
)
235 else if ( nTempVal
< nMin
)
238 rOutStr
= ImplGetCurr( rLocaleDataWrapper
, nTempVal
, nDecDigits
, rFormatter
.GetCurrencySymbol(), rFormatter
.IsUseThousandSep() );
243 void LongCurrencyFormatter::ImpInit()
250 mbThousandSep
= true;
251 SetDecimalDigits( 0 );
254 LongCurrencyFormatter::LongCurrencyFormatter(Edit
* pEdit
)
255 : FormatterBase(pEdit
)
260 LongCurrencyFormatter::~LongCurrencyFormatter()
264 void LongCurrencyFormatter::SetCurrencySymbol( const OUString
& rStr
)
266 maCurrencySymbol
= rStr
;
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
)
285 else if ( nNewValue
< mnMin
)
287 mnLastValue
= nNewValue
;
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
);
300 GetField()->SetText( aStr
);
301 MarkToBeReformatted( false );
304 BigInt
LongCurrencyFormatter::GetValue() const
310 if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue
, GetDecimalDigits(), GetLocaleDataWrapper() ) )
312 if ( nTempValue
> mnMax
)
314 else if ( nTempValue
< mnMin
)
322 void LongCurrencyFormatter::Reformat()
327 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
331 bool bOK
= ImplLongCurrencyReformat( GetField()->GetText(), mnMin
, mnMax
,
332 GetDecimalDigits(), GetLocaleDataWrapper(), aStr
, *this );
336 if ( !aStr
.isEmpty() )
338 GetField()->SetText( aStr
);
339 MarkToBeReformatted( false );
340 ImplLongCurrencyGetValue( aStr
, mnLastValue
, GetDecimalDigits(), GetLocaleDataWrapper() );
343 SetValue( mnLastValue
);
346 void LongCurrencyFormatter::ReformatAll()
351 void LongCurrencyFormatter::SetMin(const BigInt
& rNewMin
)
357 void LongCurrencyFormatter::SetMax(const BigInt
& rNewMax
)
363 void LongCurrencyFormatter::SetUseThousandSep( bool b
)
369 void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits
)
374 mnDecimalDigits
= nDigits
;
379 void ImplNewLongCurrencyFieldValue(LongCurrencyField
* pField
, const BigInt
& rNewValue
)
381 Selection aSelect
= pField
->GetSelection();
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
;
392 if ( !aSelect
.Len() )
393 aSelect
.Min() = SELECTION_MAX
;
394 aSelect
.Max() = SELECTION_MAX
;
396 pField
->SetSelection( aSelect
);
397 pField
->SetModifyFlag();
401 LongCurrencyField::LongCurrencyField(vcl::Window
* pParent
, WinBits nWinStyle
)
402 : SpinField( pParent
, nWinStyle
)
403 , LongCurrencyFormatter(this)
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() )
426 return SpinField::EventNotify( rNEvt
);
429 void LongCurrencyField::Modify()
431 MarkToBeReformatted( true );
435 void LongCurrencyField::Up()
437 BigInt nValue
= GetValue();
438 nValue
+= mnSpinSize
;
439 if ( nValue
> mnMax
)
442 ImplNewLongCurrencyFieldValue( this, nValue
);
446 void LongCurrencyField::Down()
448 BigInt nValue
= GetValue();
449 nValue
-= mnSpinSize
;
450 if ( nValue
< mnMin
)
453 ImplNewLongCurrencyFieldValue( this, nValue
);
457 void LongCurrencyField::First()
459 ImplNewLongCurrencyFieldValue( this, mnFirst
);
463 void LongCurrencyField::Last()
465 ImplNewLongCurrencyFieldValue( this, mnLast
);
469 LongCurrencyBox::LongCurrencyBox(vcl::Window
* pParent
, WinBits nWinStyle
)
470 : ComboBox(pParent
, nWinStyle
)
471 , LongCurrencyFormatter(this)
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() )
490 return ComboBox::EventNotify( rNEvt
);
493 void LongCurrencyBox::Modify()
495 MarkToBeReformatted( true );
499 void LongCurrencyBox::ReformatAll()
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(),
510 InsertEntry( aStr
, i
);
512 LongCurrencyFormatter::Reformat();
513 SetUpdateMode( true );
516 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */