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 <sot/object.hxx>
22 #include <sot/factory.hxx>
23 #include <tools/debug.hxx>
24 #include <tools/bigint.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/longcurr.hxx>
34 #include <unotools/localedatawrapper.hxx>
36 using namespace ::comphelper
;
38 #define FORMAT_LONGCURRENCY 4
40 static BigInt
ImplPower10( sal_uInt16 n
)
45 for ( i
=0; i
< n
; i
++ )
51 static XubString
ImplGetCurr( const LocaleDataWrapper
& rLocaleDataWrapper
, const BigInt
&rNumber
, sal_uInt16 nDigits
, const String
& rCurrSymbol
, sal_Bool bShowThousandSep
)
53 DBG_ASSERT( nDigits
< 10, "LongCurrency may only have 9 decimal places" );
55 if ( rNumber
.IsZero() || (long)rNumber
)
56 return rLocaleDataWrapper
.getCurr( (long)rNumber
, nDigits
, rCurrSymbol
, bShowThousandSep
);
58 BigInt
aTmp( ImplPower10( nDigits
) );
59 BigInt
aInteger( rNumber
);
62 BigInt
aFraction( rNumber
);
65 if ( !aInteger
.IsZero() )
70 if ( rNumber
.IsNeg() )
73 XubString aTemplate
= rLocaleDataWrapper
.getCurr( (long)aFraction
, nDigits
, rCurrSymbol
, bShowThousandSep
);
74 while( !aInteger
.IsZero() )
79 if( !aInteger
.IsZero() )
82 XubString aFractionStr
= rLocaleDataWrapper
.getNum( (long)aFraction
, 0 );
84 xub_StrLen nSPos
= aTemplate
.Search( '1' );
85 if ( aFractionStr
.Len() == 1 )
86 aTemplate
.SetChar( nSPos
, aFractionStr
.GetChar( 0 ) );
89 aTemplate
.Erase( nSPos
, 1 );
90 aTemplate
.Insert( aFractionStr
, nSPos
);
97 static bool ImplNumericProcessKeyInput( Edit
*, const KeyEvent
& rKEvt
,
98 sal_Bool bStrictFormat
, sal_Bool bThousandSep
,
99 const LocaleDataWrapper
& rLocaleDataWrapper
)
101 if ( !bStrictFormat
)
105 sal_Unicode cChar
= rKEvt
.GetCharCode();
106 sal_uInt16 nGroup
= rKEvt
.GetKeyCode().GetGroup();
108 if ( (nGroup
== KEYGROUP_FKEYS
) || (nGroup
== KEYGROUP_CURSOR
) ||
109 (nGroup
== KEYGROUP_MISC
) ||
110 ((cChar
>= '0') && (cChar
<= '9')) ||
111 (bThousandSep
&& string::equals(rLocaleDataWrapper
.getNumThousandSep(), cChar
)) ||
112 (string::equals(rLocaleDataWrapper
.getNumDecimalSep(), cChar
) ) ||
120 static bool ImplNumericGetValue( const XubString
& rStr
, BigInt
& rValue
,
121 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
,
122 sal_Bool bCurrency
= sal_False
)
124 XubString aStr
= rStr
;
126 OUStringBuffer aStr2
;
128 sal_Bool bNegative
= sal_False
;
134 // Trim leading and trailing spaces
135 aStr
= string::strip(aStr
, ' ');
137 // Find decimal sign's position
138 nDecPos
= aStr
.Search( rLocaleDataWrapper
.getNumDecimalSep() );
140 if ( nDecPos
!= STRING_NOTFOUND
)
142 aStr1
= aStr
.Copy( 0, nDecPos
);
143 aStr2
.append(aStr
.Copy(nDecPos
+1));
151 if ( (aStr
.GetChar( 0 ) == '(') && (aStr
.GetChar( aStr
.Len()-1 ) == ')') )
152 bNegative
= sal_True
;
155 for (xub_StrLen i
=0; i
< aStr
.Len(); i
++ )
157 if ( (aStr
.GetChar( i
) >= '0') && (aStr
.GetChar( i
) <= '9') )
159 else if ( aStr
.GetChar( i
) == '-' )
161 bNegative
= sal_True
;
166 if ( !bNegative
&& bCurrency
&& aStr
.Len() )
168 sal_uInt16 nFormat
= rLocaleDataWrapper
.getCurrNegativeFormat();
169 if ( (nFormat
== 3) || (nFormat
== 6) ||
170 (nFormat
== 7) || (nFormat
== 10) )
172 for (xub_StrLen i
= (sal_uInt16
)(aStr
.Len()-1); i
> 0; i
++ )
174 if ( (aStr
.GetChar( i
) >= '0') && (aStr
.GetChar( i
) <= '9') )
176 else if ( aStr
.GetChar( i
) == '-' )
178 bNegative
= sal_True
;
187 if ( aStr1
.GetChar( 0 ) == '-' )
188 bNegative
= sal_True
;
191 // delete unwanted characters
192 for (xub_StrLen i
=0; i
< aStr1
.Len(); )
194 if ( (aStr1
.GetChar( i
) >= '0') && (aStr1
.GetChar( i
) <= '9') )
199 for (sal_Int32 i
=0; i
< aStr2
.getLength();)
201 if ((aStr2
[i
] >= '0') && (aStr2
[i
] <= '9'))
207 if (!aStr1
.Len() && !aStr2
.getLength())
213 aStr1
.Insert( '-', 0 );
215 // Cut down decimal part and round while doing so
217 if (aStr2
.getLength() > nDecDigits
)
219 if (aStr2
[nDecDigits
] >= '5')
221 string::truncateToLength(aStr2
, nDecDigits
);
223 if (aStr2
.getLength() < nDecDigits
)
224 string::padToLength(aStr2
, nDecDigits
, '0');
227 aStr
+= aStr2
.makeStringAndClear();
230 BigInt
nValue( aStr
);
244 static bool ImplLongCurrencyProcessKeyInput( Edit
* pEdit
, const KeyEvent
& rKEvt
,
245 sal_Bool
, sal_Bool bUseThousandSep
, const LocaleDataWrapper
& rLocaleDataWrapper
)
247 // There's no StrictFormat that makes sense here, thus allow all chars
248 return ImplNumericProcessKeyInput( pEdit
, rKEvt
, sal_False
, bUseThousandSep
, rLocaleDataWrapper
);
251 inline bool ImplLongCurrencyGetValue( const XubString
& rStr
, BigInt
& rValue
,
252 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
)
254 return ImplNumericGetValue( rStr
, rValue
, nDecDigits
, rLocaleDataWrapper
, sal_True
);
257 bool ImplLongCurrencyReformat( const XubString
& rStr
, BigInt nMin
, BigInt nMax
,
258 sal_uInt16 nDecDigits
,
259 const LocaleDataWrapper
& rLocaleDataWrapper
, String
& rOutStr
,
260 LongCurrencyFormatter
& rFormatter
)
263 if ( !ImplNumericGetValue( rStr
, nValue
, nDecDigits
, rLocaleDataWrapper
, sal_True
) )
267 BigInt nTempVal
= nValue
;
268 if ( nTempVal
> nMax
)
270 else if ( nTempVal
< nMin
)
273 if ( rFormatter
.GetErrorHdl().IsSet() && (nValue
!= nTempVal
) )
275 rFormatter
.mnCorrectedValue
= nTempVal
;
276 if ( !rFormatter
.GetErrorHdl().Call( &rFormatter
) )
278 rFormatter
.mnCorrectedValue
= 0;
283 rFormatter
.mnCorrectedValue
= 0;
287 rOutStr
= ImplGetCurr( rLocaleDataWrapper
, nTempVal
, nDecDigits
, rFormatter
.GetCurrencySymbol(), rFormatter
.IsUseThousandSep() );
292 void LongCurrencyFormatter::ImpInit()
299 mnCorrectedValue
= 0;
301 mnType
= FORMAT_LONGCURRENCY
;
302 mbThousandSep
= sal_True
;
303 SetDecimalDigits( 0 );
306 LongCurrencyFormatter::LongCurrencyFormatter()
311 LongCurrencyFormatter::~LongCurrencyFormatter()
315 void LongCurrencyFormatter::SetCurrencySymbol( const String
& rStr
)
317 maCurrencySymbol
= rStr
;
321 String
LongCurrencyFormatter::GetCurrencySymbol() const
323 return !maCurrencySymbol
.isEmpty() ? maCurrencySymbol
: GetLocaleDataWrapper().getCurrSymbol();
326 void LongCurrencyFormatter::SetValue( BigInt nNewValue
)
328 SetUserValue( nNewValue
);
329 mnFieldValue
= mnLastValue
;
330 SetEmptyFieldValueData( sal_False
);
333 void LongCurrencyFormatter::SetUserValue( BigInt nNewValue
)
335 if ( nNewValue
> mnMax
)
337 else if ( nNewValue
< mnMin
)
339 mnLastValue
= nNewValue
;
344 XubString aStr
= ImplGetCurr( GetLocaleDataWrapper(), nNewValue
, GetDecimalDigits(), GetCurrencySymbol(), IsUseThousandSep() );
345 if ( GetField()->HasFocus() )
347 Selection aSelection
= GetField()->GetSelection();
348 GetField()->SetText( aStr
);
349 GetField()->SetSelection( aSelection
);
352 GetField()->SetText( aStr
);
353 MarkToBeReformatted( sal_False
);
356 BigInt
LongCurrencyFormatter::GetValue() const
362 if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue
, GetDecimalDigits(), GetLocaleDataWrapper() ) )
364 if ( nTempValue
> mnMax
)
366 else if ( nTempValue
< mnMin
)
374 void LongCurrencyFormatter::Reformat()
379 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
383 bool bOK
= ImplLongCurrencyReformat( GetField()->GetText(), mnMin
, mnMax
,
384 GetDecimalDigits(), GetLocaleDataWrapper(), aStr
, *this );
390 GetField()->SetText( aStr
);
391 MarkToBeReformatted( sal_False
);
392 ImplLongCurrencyGetValue( aStr
, mnLastValue
, GetDecimalDigits(), GetLocaleDataWrapper() );
395 SetValue( mnLastValue
);
398 void LongCurrencyFormatter::ReformatAll()
403 void LongCurrencyFormatter::SetMin( BigInt nNewMin
)
409 void LongCurrencyFormatter::SetMax( BigInt nNewMax
)
415 void LongCurrencyFormatter::SetUseThousandSep( sal_Bool b
)
421 void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits
)
426 mnDecimalDigits
= nDigits
;
430 sal_uInt16
LongCurrencyFormatter::GetDecimalDigits() const
432 return mnDecimalDigits
;
435 void ImplNewLongCurrencyFieldValue( LongCurrencyField
* pField
, BigInt nNewValue
)
437 Selection aSelect
= pField
->GetSelection();
439 XubString aText
= pField
->GetText();
440 bool bLastSelected
= ((xub_StrLen
)aSelect
.Max() == aText
.Len());
442 BigInt nOldLastValue
= pField
->mnLastValue
;
443 pField
->SetUserValue( nNewValue
);
444 pField
->mnLastValue
= nOldLastValue
;
448 if ( !aSelect
.Len() )
449 aSelect
.Min() = SELECTION_MAX
;
450 aSelect
.Max() = SELECTION_MAX
;
452 pField
->SetSelection( aSelect
);
453 pField
->SetModifyFlag();
457 LongCurrencyField::LongCurrencyField( Window
* pParent
, WinBits nWinStyle
) :
458 SpinField( pParent
, nWinStyle
)
469 LongCurrencyField::~LongCurrencyField()
474 long LongCurrencyField::PreNotify( NotifyEvent
& rNEvt
)
476 if( rNEvt
.GetType() == EVENT_KEYINPUT
)
478 if ( ImplLongCurrencyProcessKeyInput( GetField(), *rNEvt
.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), GetLocaleDataWrapper() ) )
481 return SpinField::PreNotify( rNEvt
);
485 long LongCurrencyField::Notify( NotifyEvent
& rNEvt
)
487 if( rNEvt
.GetType() == EVENT_GETFOCUS
)
489 MarkToBeReformatted( sal_False
);
491 else if( rNEvt
.GetType() == EVENT_LOSEFOCUS
)
493 if ( MustBeReformatted() )
499 return SpinField::Notify( rNEvt
);
503 void LongCurrencyField::Modify()
505 MarkToBeReformatted( sal_True
);
510 void LongCurrencyField::Up()
512 BigInt nValue
= GetValue();
513 nValue
+= mnSpinSize
;
514 if ( nValue
> mnMax
)
517 ImplNewLongCurrencyFieldValue( this, nValue
);
521 void LongCurrencyField::Down()
523 BigInt nValue
= GetValue();
524 nValue
-= mnSpinSize
;
525 if ( nValue
< mnMin
)
528 ImplNewLongCurrencyFieldValue( this, nValue
);
533 void LongCurrencyField::First()
535 ImplNewLongCurrencyFieldValue( this, mnFirst
);
540 void LongCurrencyField::Last()
542 ImplNewLongCurrencyFieldValue( this, mnLast
);
546 LongCurrencyBox::LongCurrencyBox( Window
* pParent
, WinBits nWinStyle
) :
547 ComboBox( pParent
, nWinStyle
)
553 LongCurrencyBox::~LongCurrencyBox()
557 long LongCurrencyBox::PreNotify( NotifyEvent
& rNEvt
)
559 if( rNEvt
.GetType() == EVENT_KEYINPUT
)
561 if ( ImplLongCurrencyProcessKeyInput( GetField(), *rNEvt
.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), GetLocaleDataWrapper() ) )
564 return ComboBox::PreNotify( rNEvt
);
568 long LongCurrencyBox::Notify( NotifyEvent
& rNEvt
)
570 if( rNEvt
.GetType() == EVENT_GETFOCUS
)
572 MarkToBeReformatted( sal_False
);
574 else if( rNEvt
.GetType() == EVENT_LOSEFOCUS
)
576 if ( MustBeReformatted() )
582 return ComboBox::Notify( rNEvt
);
585 void LongCurrencyBox::Modify()
587 MarkToBeReformatted( sal_True
);
591 void LongCurrencyBox::ReformatAll()
594 SetUpdateMode( sal_False
);
595 sal_uInt16 nEntryCount
= GetEntryCount();
596 for ( sal_uInt16 i
=0; i
< nEntryCount
; i
++ )
598 ImplLongCurrencyReformat( GetEntry( i
), mnMin
, mnMax
,
599 GetDecimalDigits(), GetLocaleDataWrapper(),
602 InsertEntry( aStr
, i
);
604 LongCurrencyFormatter::Reformat();
605 SetUpdateMode( sal_True
);
608 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */