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 <comphelper/string.hxx>
21 #include <tools/debug.hxx>
22 #include <tools/bigint.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/longcurr.hxx>
32 #include <unotools/localedatawrapper.hxx>
34 using namespace ::comphelper
;
39 BigInt
ImplPower10( sal_uInt16 n
)
44 for ( i
=0; i
< n
; i
++ )
50 OUString
ImplGetCurr( const LocaleDataWrapper
& rLocaleDataWrapper
, const BigInt
&rNumber
, sal_uInt16 nDigits
, const OUString
& rCurrSymbol
, bool bShowThousandSep
)
52 SAL_WARN_IF( nDigits
>= 10, "vcl", "LongCurrency may only have 9 decimal places" );
54 if ( rNumber
.IsZero() || (long)rNumber
)
55 return rLocaleDataWrapper
.getCurr( (long)rNumber
, nDigits
, rCurrSymbol
, bShowThousandSep
);
57 BigInt
aTmp( ImplPower10( nDigits
) );
58 BigInt
aInteger( rNumber
);
61 BigInt
aFraction( rNumber
);
64 if ( !aInteger
.IsZero() )
69 if ( rNumber
.IsNeg() )
72 OUStringBuffer aTemplate
= rLocaleDataWrapper
.getCurr( (long)aFraction
, nDigits
, rCurrSymbol
, bShowThousandSep
);
73 while( !aInteger
.IsZero() )
78 if( !aInteger
.IsZero() )
81 OUString aFractionStr
= rLocaleDataWrapper
.getNum( (long)aFraction
, 0 );
83 sal_Int32 nSPos
= aTemplate
.indexOf( '1' );
86 if ( aFractionStr
.getLength() == 1 )
87 aTemplate
[ nSPos
] = aFractionStr
[0];
90 aTemplate
.remove( nSPos
, 1 );
91 aTemplate
.insert( nSPos
, aFractionStr
);
95 return aTemplate
.makeStringAndClear();
98 bool ImplNumericProcessKeyInput( Edit
*, const KeyEvent
& rKEvt
,
99 bool bStrictFormat
, bool bThousandSep
,
100 const LocaleDataWrapper
& rLocaleDataWrapper
)
102 if ( !bStrictFormat
)
106 sal_Unicode cChar
= rKEvt
.GetCharCode();
107 sal_uInt16 nGroup
= rKEvt
.GetKeyCode().GetGroup();
109 if ( (nGroup
== KEYGROUP_FKEYS
) || (nGroup
== KEYGROUP_CURSOR
) ||
110 (nGroup
== KEYGROUP_MISC
) ||
111 ((cChar
>= '0') && (cChar
<= '9')) ||
112 (bThousandSep
&& string::equals(rLocaleDataWrapper
.getNumThousandSep(), cChar
)) ||
113 (string::equals(rLocaleDataWrapper
.getNumDecimalSep(), cChar
) ) ||
121 bool ImplNumericGetValue( const OUString
& rStr
, BigInt
& rValue
,
122 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
,
125 OUString aStr
= rStr
;
126 OUStringBuffer aStr1
;
127 OUStringBuffer aStr2
;
129 bool bNegative
= false;
132 if ( rStr
.isEmpty() )
135 // Trim leading and trailing spaces
136 aStr
= string::strip(aStr
, ' ');
138 // Find decimal sign's position
139 nDecPos
= aStr
.indexOf( rLocaleDataWrapper
.getNumDecimalSep() );
143 aStr1
= aStr
.copy( 0, nDecPos
);
144 aStr2
.append(aStr
.copy(nDecPos
+1));
152 if ( (aStr
[ 0 ] == '(') && (aStr
[ aStr
.getLength()-1 ] == ')') )
156 for (sal_Int32 i
=0; i
< aStr
.getLength(); i
++ )
158 if ( (aStr
[ i
] >= '0') && (aStr
[ i
] <= '9') )
160 else if ( aStr
[ i
] == '-' )
167 if ( !bNegative
&& bCurrency
&& !aStr
.isEmpty() )
169 sal_uInt16 nFormat
= rLocaleDataWrapper
.getCurrNegativeFormat();
170 if ( (nFormat
== 3) || (nFormat
== 6) ||
171 (nFormat
== 7) || (nFormat
== 10) )
173 for (sal_Int32 i
= aStr
.getLength()-1; i
> 0; i
++ )
175 if ( (aStr
[ i
] >= '0') && (aStr
[ i
] <= '9') )
177 else if ( aStr
[ i
] == '-' )
188 if ( aStr1
[ 0 ] == '-' )
192 // delete unwanted characters
193 for (sal_Int32 i
=0; i
< aStr1
.getLength(); )
195 if ( (aStr1
[ i
] >= '0') && (aStr1
[ i
] <= '9') )
198 aStr1
.remove( i
, 1 );
200 for (sal_Int32 i
=0; i
< aStr2
.getLength(); )
202 if ((aStr2
[i
] >= '0') && (aStr2
[i
] <= '9'))
208 if ( aStr1
.isEmpty() && aStr2
.isEmpty())
211 if ( aStr1
.isEmpty() )
214 aStr1
.insert( 0, '-');
216 // Cut down decimal part and round while doing so
218 if (aStr2
.getLength() > nDecDigits
)
220 if (aStr2
[nDecDigits
] >= '5')
222 string::truncateToLength(aStr2
, nDecDigits
);
224 if (aStr2
.getLength() < nDecDigits
)
225 string::padToLength(aStr2
, nDecDigits
, '0');
227 aStr
= aStr1
.makeStringAndClear();
228 aStr
+= aStr2
.makeStringAndClear();
231 BigInt
nValue( aStr
);
245 bool ImplLongCurrencyProcessKeyInput( Edit
* pEdit
, const KeyEvent
& rKEvt
,
246 bool, bool bUseThousandSep
, const LocaleDataWrapper
& rLocaleDataWrapper
)
248 // There's no StrictFormat that makes sense here, thus allow all chars
249 return ImplNumericProcessKeyInput( pEdit
, rKEvt
, false, bUseThousandSep
, rLocaleDataWrapper
);
254 inline bool ImplLongCurrencyGetValue( const OUString
& rStr
, BigInt
& rValue
,
255 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
)
257 return ImplNumericGetValue( rStr
, rValue
, nDecDigits
, rLocaleDataWrapper
, true );
260 bool ImplLongCurrencyReformat( const OUString
& rStr
, BigInt
const & nMin
, BigInt
const & nMax
,
261 sal_uInt16 nDecDigits
,
262 const LocaleDataWrapper
& rLocaleDataWrapper
, OUString
& rOutStr
,
263 LongCurrencyFormatter
& rFormatter
)
266 if ( !ImplNumericGetValue( rStr
, nValue
, nDecDigits
, rLocaleDataWrapper
, true ) )
270 BigInt nTempVal
= nValue
;
271 if ( nTempVal
> nMax
)
273 else if ( nTempVal
< nMin
)
276 rOutStr
= ImplGetCurr( rLocaleDataWrapper
, nTempVal
, nDecDigits
, rFormatter
.GetCurrencySymbol(), rFormatter
.IsUseThousandSep() );
281 void LongCurrencyFormatter::ImpInit()
288 mnCorrectedValue
= 0;
290 mbThousandSep
= true;
291 SetDecimalDigits( 0 );
294 LongCurrencyFormatter::LongCurrencyFormatter()
299 LongCurrencyFormatter::~LongCurrencyFormatter()
303 void LongCurrencyFormatter::SetCurrencySymbol( const OUString
& rStr
)
305 maCurrencySymbol
= rStr
;
309 OUString
const & LongCurrencyFormatter::GetCurrencySymbol() const
311 return !maCurrencySymbol
.isEmpty() ? maCurrencySymbol
: GetLocaleDataWrapper().getCurrSymbol();
314 void LongCurrencyFormatter::SetValue(const BigInt
& rNewValue
)
316 SetUserValue(rNewValue
);
317 mnFieldValue
= mnLastValue
;
318 SetEmptyFieldValueData( false );
321 void LongCurrencyFormatter::SetUserValue( BigInt nNewValue
)
323 if ( nNewValue
> mnMax
)
325 else if ( nNewValue
< mnMin
)
327 mnLastValue
= nNewValue
;
332 OUString aStr
= ImplGetCurr( GetLocaleDataWrapper(), nNewValue
, GetDecimalDigits(), GetCurrencySymbol(), IsUseThousandSep() );
333 if ( GetField()->HasFocus() )
335 Selection aSelection
= GetField()->GetSelection();
336 GetField()->SetText( aStr
);
337 GetField()->SetSelection( aSelection
);
340 GetField()->SetText( aStr
);
341 MarkToBeReformatted( false );
344 BigInt
LongCurrencyFormatter::GetValue() const
350 if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue
, GetDecimalDigits(), GetLocaleDataWrapper() ) )
352 if ( nTempValue
> mnMax
)
354 else if ( nTempValue
< mnMin
)
362 void LongCurrencyFormatter::Reformat()
367 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
371 bool bOK
= ImplLongCurrencyReformat( GetField()->GetText(), mnMin
, mnMax
,
372 GetDecimalDigits(), GetLocaleDataWrapper(), aStr
, *this );
376 if ( !aStr
.isEmpty() )
378 GetField()->SetText( aStr
);
379 MarkToBeReformatted( false );
380 ImplLongCurrencyGetValue( aStr
, mnLastValue
, GetDecimalDigits(), GetLocaleDataWrapper() );
383 SetValue( mnLastValue
);
386 void LongCurrencyFormatter::ReformatAll()
391 void LongCurrencyFormatter::SetMin(const BigInt
& rNewMin
)
397 void LongCurrencyFormatter::SetMax(const BigInt
& rNewMax
)
403 void LongCurrencyFormatter::SetUseThousandSep( bool b
)
409 void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits
)
414 mnDecimalDigits
= nDigits
;
419 void ImplNewLongCurrencyFieldValue(LongCurrencyField
* pField
, const BigInt
& rNewValue
)
421 Selection aSelect
= pField
->GetSelection();
423 OUString aText
= pField
->GetText();
424 bool bLastSelected
= aSelect
.Max() == aText
.getLength();
426 BigInt nOldLastValue
= pField
->mnLastValue
;
427 pField
->SetUserValue(rNewValue
);
428 pField
->mnLastValue
= nOldLastValue
;
432 if ( !aSelect
.Len() )
433 aSelect
.Min() = SELECTION_MAX
;
434 aSelect
.Max() = SELECTION_MAX
;
436 pField
->SetSelection( aSelect
);
437 pField
->SetModifyFlag();
441 LongCurrencyField::LongCurrencyField( vcl::Window
* pParent
, WinBits nWinStyle
) :
442 SpinField( pParent
, nWinStyle
)
452 bool LongCurrencyField::PreNotify( NotifyEvent
& rNEvt
)
454 if( rNEvt
.GetType() == MouseNotifyEvent::KEYINPUT
)
456 if ( ImplLongCurrencyProcessKeyInput( GetField(), *rNEvt
.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), GetLocaleDataWrapper() ) )
459 return SpinField::PreNotify( rNEvt
);
462 bool LongCurrencyField::EventNotify( NotifyEvent
& rNEvt
)
464 if( rNEvt
.GetType() == MouseNotifyEvent::GETFOCUS
)
466 MarkToBeReformatted( false );
468 else if( rNEvt
.GetType() == MouseNotifyEvent::LOSEFOCUS
)
470 if ( MustBeReformatted() )
476 return SpinField::EventNotify( rNEvt
);
479 void LongCurrencyField::Modify()
481 MarkToBeReformatted( true );
485 void LongCurrencyField::Up()
487 BigInt nValue
= GetValue();
488 nValue
+= mnSpinSize
;
489 if ( nValue
> mnMax
)
492 ImplNewLongCurrencyFieldValue( this, nValue
);
496 void LongCurrencyField::Down()
498 BigInt nValue
= GetValue();
499 nValue
-= mnSpinSize
;
500 if ( nValue
< mnMin
)
503 ImplNewLongCurrencyFieldValue( this, nValue
);
507 void LongCurrencyField::First()
509 ImplNewLongCurrencyFieldValue( this, mnFirst
);
513 void LongCurrencyField::Last()
515 ImplNewLongCurrencyFieldValue( this, mnLast
);
519 LongCurrencyBox::LongCurrencyBox( vcl::Window
* pParent
, WinBits nWinStyle
) :
520 ComboBox( pParent
, nWinStyle
)
526 bool LongCurrencyBox::PreNotify( NotifyEvent
& rNEvt
)
528 if( rNEvt
.GetType() == MouseNotifyEvent::KEYINPUT
)
530 if ( ImplLongCurrencyProcessKeyInput( GetField(), *rNEvt
.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), GetLocaleDataWrapper() ) )
533 return ComboBox::PreNotify( rNEvt
);
536 bool LongCurrencyBox::EventNotify( NotifyEvent
& rNEvt
)
538 if( rNEvt
.GetType() == MouseNotifyEvent::GETFOCUS
)
540 MarkToBeReformatted( false );
542 else if( rNEvt
.GetType() == MouseNotifyEvent::LOSEFOCUS
)
544 if ( MustBeReformatted() )
550 return ComboBox::EventNotify( rNEvt
);
553 void LongCurrencyBox::Modify()
555 MarkToBeReformatted( true );
559 void LongCurrencyBox::ReformatAll()
562 SetUpdateMode( false );
563 const sal_Int32 nEntryCount
= GetEntryCount();
564 for ( sal_Int32 i
=0; i
< nEntryCount
; ++i
)
566 ImplLongCurrencyReformat( GetEntry( i
), mnMin
, mnMax
,
567 GetDecimalDigits(), GetLocaleDataWrapper(),
570 InsertEntry( aStr
, i
);
572 LongCurrencyFormatter::Reformat();
573 SetUpdateMode( true );
576 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */