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/svapp.hxx>
30 #include <vcl/toolkit/longcurr.hxx>
31 #include <vcl/weldutils.hxx>
33 #include <unotools/localedatawrapper.hxx>
35 using namespace ::comphelper
;
40 BigInt
ImplPower10( sal_uInt16 n
)
45 for ( i
=0; i
< n
; i
++ )
51 OUString
ImplGetCurr( const LocaleDataWrapper
& rLocaleDataWrapper
, const BigInt
&rNumber
, sal_uInt16 nDigits
, std::u16string_view rCurrSymbol
, bool bShowThousandSep
)
53 SAL_WARN_IF( nDigits
>= 10, "vcl", "LongCurrency may only have 9 decimal places" );
55 if ( rNumber
.IsZero() || static_cast<tools::Long
>(rNumber
) )
56 return rLocaleDataWrapper
.getCurr( static_cast<tools::Long
>(rNumber
), nDigits
, rCurrSymbol
, bShowThousandSep
);
58 BigInt
aTmp( ImplPower10( nDigits
) );
61 rNumber
.Abs().DivMod(aTmp
, &aInteger
, &aFraction
);
62 if ( !aInteger
.IsZero() )
67 if ( rNumber
.IsNeg() )
70 OUStringBuffer
aTemplate(rLocaleDataWrapper
.getCurr( static_cast<tools::Long
>(aFraction
), nDigits
, rCurrSymbol
, bShowThousandSep
));
71 while( !aInteger
.IsZero() )
73 aInteger
.DivMod(aTmp
, &aInteger
, &aFraction
);
74 if( !aInteger
.IsZero() )
77 OUString aFractionStr
= rLocaleDataWrapper
.getNum( static_cast<tools::Long
>(aFraction
), 0 );
79 sal_Int32 nSPos
= aTemplate
.indexOf( '1' );
82 if ( aFractionStr
.getLength() == 1 )
83 aTemplate
[ nSPos
] = aFractionStr
[0];
86 aTemplate
.remove( nSPos
, 1 );
87 aTemplate
.insert( nSPos
, aFractionStr
);
91 return aTemplate
.makeStringAndClear();
94 bool ImplCurrencyGetValue( const OUString
& rStr
, BigInt
& rValue
,
95 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
)
101 bool bNegative
= false;
104 if ( rStr
.isEmpty() )
107 // Trim leading and trailing spaces
108 aStr
= string::strip(aStr
, ' ');
110 // Find decimal sign's position
111 nDecPos
= aStr
.indexOf( rLocaleDataWrapper
.getNumDecimalSep() );
112 if (nDecPos
< 0 && !rLocaleDataWrapper
.getNumDecimalSepAlt().isEmpty())
113 nDecPos
= aStr
.indexOf( rLocaleDataWrapper
.getNumDecimalSepAlt() );
117 aStr1
= aStr
.subView( 0, nDecPos
);
118 aStr2
.append(aStr
.subView(nDecPos
+1));
124 if ( (aStr
[ 0 ] == '(') && (aStr
[ aStr
.getLength()-1 ] == ')') )
128 for (sal_Int32 i
=0; i
< aStr
.getLength(); i
++ )
130 if ( (aStr
[ i
] >= '0') && (aStr
[ i
] <= '9') )
132 else if ( aStr
[ i
] == '-' )
139 if ( !bNegative
&& !aStr
.isEmpty() )
141 sal_uInt16 nFormat
= rLocaleDataWrapper
.getCurrNegativeFormat();
142 if ( (nFormat
== 3) || (nFormat
== 6) ||
143 (nFormat
== 7) || (nFormat
== 10) )
145 for (sal_Int32 i
= aStr
.getLength()-1; i
> 0; i
++ )
147 if ( (aStr
[ i
] >= '0') && (aStr
[ i
] <= '9') )
149 else if ( aStr
[ i
] == '-' )
158 // delete unwanted characters
159 for (sal_Int32 i
=0; i
< aStr1
.getLength(); )
161 if ( (aStr1
[ i
] >= '0') && (aStr1
[ i
] <= '9') )
164 aStr1
.remove( i
, 1 );
166 for (sal_Int32 i
=0; i
< aStr2
.getLength(); )
168 if ((aStr2
[i
] >= '0') && (aStr2
[i
] <= '9'))
174 if ( aStr1
.isEmpty() && aStr2
.isEmpty())
177 if ( aStr1
.isEmpty() )
180 aStr1
.insert( 0, '-');
182 // Cut down decimal part and round while doing so
184 if (aStr2
.getLength() > nDecDigits
)
186 if (aStr2
[nDecDigits
] >= '5')
188 string::truncateToLength(aStr2
, nDecDigits
);
190 string::padToLength(aStr2
, nDecDigits
, '0');
193 aStr
= aStr1
.makeStringAndClear();
196 BigInt
nValue( aStr
);
212 static bool ImplLongCurrencyGetValue( const OUString
& rStr
, BigInt
& rValue
,
213 sal_uInt16 nDecDigits
, const LocaleDataWrapper
& rLocaleDataWrapper
)
215 return ImplCurrencyGetValue( rStr
, rValue
, nDecDigits
, rLocaleDataWrapper
);
220 IMPL_LINK_NOARG(LongCurrencyFormatter
, FormatOutputHdl
, LinkParamNone
*, bool)
222 const LocaleDataWrapper
& rLocaleDataWrapper
= Application::GetSettings().GetLocaleDataWrapper();
223 const OUString
& rCurrencySymbol
= !m_aCurrencySymbol
.isEmpty() ? m_aCurrencySymbol
: rLocaleDataWrapper
.getCurrSymbol();
224 double fValue
= GetValue();
225 sal_uInt16 nDecimalDigits
= GetDecimalDigits();
228 // tdf#158669 round to decimal digits
229 fValue
= std::round(fValue
* weld::SpinButton::Power10(nDecimalDigits
));
231 OUString aText
= ImplGetCurr(rLocaleDataWrapper
, fValue
, GetDecimalDigits(), rCurrencySymbol
, m_bThousandSep
);
232 ImplSetTextImpl(aText
, nullptr);
236 IMPL_LINK(LongCurrencyFormatter
, ParseInputHdl
, sal_Int64
*, result
, TriState
)
238 const LocaleDataWrapper
& rLocaleDataWrapper
= Application::GetSettings().GetLocaleDataWrapper();
241 bool bRet
= ImplLongCurrencyGetValue(GetEntryText(), value
, GetDecimalDigits(), rLocaleDataWrapper
);
244 *result
= double(value
);
246 return bRet
? TRISTATE_TRUE
: TRISTATE_FALSE
;
250 bool ImplLongCurrencyReformat( const OUString
& rStr
, BigInt
const & nMin
, BigInt
const & nMax
,
251 sal_uInt16 nDecDigits
,
252 const LocaleDataWrapper
& rLocaleDataWrapper
, OUString
& rOutStr
,
253 LongCurrencyFormatter
const & rFormatter
)
256 if ( !ImplCurrencyGetValue( rStr
, nValue
, nDecDigits
, rLocaleDataWrapper
) )
260 BigInt nTempVal
= nValue
;
261 if ( nTempVal
> nMax
)
263 else if ( nTempVal
< nMin
)
266 rOutStr
= ImplGetCurr( rLocaleDataWrapper
, nTempVal
, nDecDigits
, rFormatter
.GetCurrencySymbol(), /*IsUseThousandSep*/true );
271 void LongCurrencyFormatter::ImpInit()
278 SetDecimalDigits( 0 );
281 LongCurrencyFormatter::LongCurrencyFormatter(Edit
* pEdit
)
282 : FormatterBase(pEdit
)
287 LongCurrencyFormatter::~LongCurrencyFormatter()
291 OUString
const & LongCurrencyFormatter::GetCurrencySymbol() const
293 return GetLocaleDataWrapper().getCurrSymbol();
296 void LongCurrencyFormatter::SetValue(const BigInt
& rNewValue
)
298 SetUserValue(rNewValue
);
299 SetEmptyFieldValueData( false );
302 void LongCurrencyFormatter::SetUserValue( BigInt nNewValue
)
304 if ( nNewValue
> mnMax
)
306 else if ( nNewValue
< mnMin
)
308 mnLastValue
= nNewValue
;
313 OUString aStr
= ImplGetCurr( GetLocaleDataWrapper(), nNewValue
, GetDecimalDigits(), GetCurrencySymbol(), /*UseThousandSep*/true );
314 if ( GetField()->HasFocus() )
316 Selection aSelection
= GetField()->GetSelection();
317 GetField()->SetText( aStr
);
318 GetField()->SetSelection( aSelection
);
321 GetField()->SetText( aStr
);
322 MarkToBeReformatted( false );
325 BigInt
LongCurrencyFormatter::GetValue() const
331 if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue
, GetDecimalDigits(), GetLocaleDataWrapper() ) )
333 if ( nTempValue
> mnMax
)
335 else if ( nTempValue
< mnMin
)
343 void LongCurrencyFormatter::Reformat()
348 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
352 bool bOK
= ImplLongCurrencyReformat( GetField()->GetText(), mnMin
, mnMax
,
353 GetDecimalDigits(), GetLocaleDataWrapper(), aStr
, *this );
357 if ( !aStr
.isEmpty() )
359 GetField()->SetText( aStr
);
360 MarkToBeReformatted( false );
361 ImplLongCurrencyGetValue( aStr
, mnLastValue
, GetDecimalDigits(), GetLocaleDataWrapper() );
364 SetValue( mnLastValue
);
367 void LongCurrencyFormatter::ReformatAll()
372 void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits
)
377 mnDecimalDigits
= nDigits
;
383 LongCurrencyBox::LongCurrencyBox(vcl::Window
* pParent
, WinBits nWinStyle
)
384 : ComboBox(pParent
, nWinStyle
)
385 , LongCurrencyFormatter(this)
390 bool LongCurrencyBox::EventNotify( NotifyEvent
& rNEvt
)
392 if( rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
394 MarkToBeReformatted( false );
396 else if( rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
)
398 if ( MustBeReformatted() )
404 return ComboBox::EventNotify( rNEvt
);
407 void LongCurrencyBox::Modify()
409 MarkToBeReformatted( true );
413 void LongCurrencyBox::ReformatAll()
416 SetUpdateMode( false );
417 const sal_Int32 nEntryCount
= GetEntryCount();
418 for ( sal_Int32 i
=0; i
< nEntryCount
; ++i
)
420 ImplLongCurrencyReformat( GetEntry( i
), mnMin
, mnMax
,
421 GetDecimalDigits(), GetLocaleDataWrapper(),
424 InsertEntry( aStr
, i
);
426 LongCurrencyFormatter::Reformat();
427 SetUpdateMode( true );
430 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */