LibreOffice Module vcl (master) 1
longcurr.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21
22#include <string_view>
23
24#include <comphelper/string.hxx>
25#include <tools/bigint.hxx>
26#include <sal/log.hxx>
27
28#include <vcl/event.hxx>
29#include <vcl/svapp.hxx>
31#include <vcl/weldutils.hxx>
32
34
35using namespace ::comphelper;
36
37namespace
38{
39
40BigInt ImplPower10( sal_uInt16 n )
41{
42 sal_uInt16 i;
43 BigInt nValue = 1;
44
45 for ( i=0; i < n; i++ )
46 nValue *= 10;
47
48 return nValue;
49}
50
51OUString ImplGetCurr( const LocaleDataWrapper& rLocaleDataWrapper, const BigInt &rNumber, sal_uInt16 nDigits, std::u16string_view rCurrSymbol, bool bShowThousandSep )
52{
53 SAL_WARN_IF( nDigits >= 10, "vcl", "LongCurrency may only have 9 decimal places" );
54
55 if ( rNumber.IsZero() || static_cast<tools::Long>(rNumber) )
56 return rLocaleDataWrapper.getCurr( static_cast<tools::Long>(rNumber), nDigits, rCurrSymbol, bShowThousandSep );
57
58 BigInt aTmp( ImplPower10( nDigits ) );
59 BigInt aInteger( rNumber );
60 aInteger.Abs();
61 aInteger /= aTmp;
62 BigInt aFraction( rNumber );
63 aFraction.Abs();
64 aFraction %= aTmp;
65 if ( !aInteger.IsZero() )
66 {
67 aFraction += aTmp;
68 aTmp = 1000000000;
69 }
70 if ( rNumber.IsNeg() )
71 aFraction *= -1;
72
73 OUStringBuffer aTemplate(rLocaleDataWrapper.getCurr( static_cast<tools::Long>(aFraction), nDigits, rCurrSymbol, bShowThousandSep ));
74 while( !aInteger.IsZero() )
75 {
76 aFraction = aInteger;
77 aFraction %= aTmp;
78 aInteger /= aTmp;
79 if( !aInteger.IsZero() )
80 aFraction += aTmp;
81
82 OUString aFractionStr = rLocaleDataWrapper.getNum( static_cast<tools::Long>(aFraction), 0 );
83
84 sal_Int32 nSPos = aTemplate.indexOf( '1' );
85 if (nSPos == -1)
86 break;
87 if ( aFractionStr.getLength() == 1 )
88 aTemplate[ nSPos ] = aFractionStr[0];
89 else
90 {
91 aTemplate.remove( nSPos, 1 );
92 aTemplate.insert( nSPos, aFractionStr );
93 }
94 }
95
96 return aTemplate.makeStringAndClear();
97}
98
99bool ImplCurrencyGetValue( const OUString& rStr, BigInt& rValue,
100 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper )
101{
102 OUString aStr = rStr;
103 OUStringBuffer aStr1;
104 OUStringBuffer aStr2;
105 sal_Int32 nDecPos;
106 bool bNegative = false;
107
108 // On empty string
109 if ( rStr.isEmpty() )
110 return false;
111
112 // Trim leading and trailing spaces
113 aStr = string::strip(aStr, ' ');
114
115 // Find decimal sign's position
116 nDecPos = aStr.indexOf( rLocaleDataWrapper.getNumDecimalSep() );
117 if (nDecPos < 0 && !rLocaleDataWrapper.getNumDecimalSepAlt().isEmpty())
118 nDecPos = aStr.indexOf( rLocaleDataWrapper.getNumDecimalSepAlt() );
119
120 if ( nDecPos != -1 )
121 {
122 aStr1 = aStr.subView( 0, nDecPos );
123 aStr2.append(aStr.subView(nDecPos+1));
124 }
125 else
126 aStr1 = aStr;
127
128 // Negative?
129 if ( (aStr[ 0 ] == '(') && (aStr[ aStr.getLength()-1 ] == ')') )
130 bNegative = true;
131 if ( !bNegative )
132 {
133 for (sal_Int32 i=0; i < aStr.getLength(); i++ )
134 {
135 if ( (aStr[ i ] >= '0') && (aStr[ i ] <= '9') )
136 break;
137 else if ( aStr[ i ] == '-' )
138 {
139 bNegative = true;
140 break;
141 }
142 }
143 }
144 if ( !bNegative && !aStr.isEmpty() )
145 {
146 sal_uInt16 nFormat = rLocaleDataWrapper.getCurrNegativeFormat();
147 if ( (nFormat == 3) || (nFormat == 6) ||
148 (nFormat == 7) || (nFormat == 10) )
149 {
150 for (sal_Int32 i = aStr.getLength()-1; i > 0; i++ )
151 {
152 if ( (aStr[ i ] >= '0') && (aStr[ i ] <= '9') )
153 break;
154 else if ( aStr[ i ] == '-' )
155 {
156 bNegative = true;
157 break;
158 }
159 }
160 }
161 }
162
163 // delete unwanted characters
164 for (sal_Int32 i=0; i < aStr1.getLength(); )
165 {
166 if ( (aStr1[ i ] >= '0') && (aStr1[ i ] <= '9') )
167 i++;
168 else
169 aStr1.remove( i, 1 );
170 }
171 for (sal_Int32 i=0; i < aStr2.getLength(); )
172 {
173 if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
174 ++i;
175 else
176 aStr2.remove(i, 1);
177 }
178
179 if ( aStr1.isEmpty() && aStr2.isEmpty())
180 return false;
181
182 if ( aStr1.isEmpty() )
183 aStr1 = "0";
184 if ( bNegative )
185 aStr1.insert( 0, '-');
186
187 // Cut down decimal part and round while doing so
188 bool bRound = false;
189 if (aStr2.getLength() > nDecDigits)
190 {
191 if (aStr2[nDecDigits] >= '5')
192 bRound = true;
193 string::truncateToLength(aStr2, nDecDigits);
194 }
195 string::padToLength(aStr2, nDecDigits, '0');
196
197 aStr1.append(aStr2);
198 aStr = aStr1.makeStringAndClear();
199
200 // check range
201 BigInt nValue( aStr );
202 if ( bRound )
203 {
204 if ( !bNegative )
205 nValue+=1;
206 else
207 nValue-=1;
208 }
209
210 rValue = nValue;
211
212 return true;
213}
214
215} // namespace
216
217static bool ImplLongCurrencyGetValue( const OUString& rStr, BigInt& rValue,
218 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper )
219{
220 return ImplCurrencyGetValue( rStr, rValue, nDecDigits, rLocaleDataWrapper );
221}
222
223namespace weld
224{
226 {
228 const OUString& rCurrencySymbol = !m_aCurrencySymbol.isEmpty() ? m_aCurrencySymbol : rLocaleDataWrapper.getCurrSymbol();
229 double fValue = GetValue() * weld::SpinButton::Power10(GetDecimalDigits());
230 OUString aText = ImplGetCurr(rLocaleDataWrapper, fValue, GetDecimalDigits(), rCurrencySymbol, m_bThousandSep);
231 ImplSetTextImpl(aText, nullptr);
232 return true;
233 }
234
235 IMPL_LINK(LongCurrencyFormatter, ParseInputHdl, sal_Int64*, result, TriState)
236 {
238
240 bool bRet = ImplLongCurrencyGetValue(GetEntryText(), value, GetDecimalDigits(), rLocaleDataWrapper);
241
242 if (bRet)
243 *result = double(value);
244
245 return bRet ? TRISTATE_TRUE : TRISTATE_FALSE;
246 }
247}
248
249bool ImplLongCurrencyReformat( const OUString& rStr, BigInt const & nMin, BigInt const & nMax,
250 sal_uInt16 nDecDigits,
251 const LocaleDataWrapper& rLocaleDataWrapper, OUString& rOutStr,
252 LongCurrencyFormatter const & rFormatter )
253{
255 if ( !ImplCurrencyGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper ) )
256 return true;
257 else
258 {
259 BigInt nTempVal = nValue;
260 if ( nTempVal > nMax )
261 nTempVal = nMax;
262 else if ( nTempVal < nMin )
263 nTempVal = nMin;
264
265 rOutStr = ImplGetCurr( rLocaleDataWrapper, nTempVal, nDecDigits, rFormatter.GetCurrencySymbol(), /*IsUseThousandSep*/true );
266 return true;
267 }
268}
269
270void LongCurrencyFormatter::ImpInit()
271{
272 mnLastValue = 0;
273 mnMin = 0;
274 mnMax = 0x7FFFFFFF;
275 mnMax *= 0x7FFFFFFF;
276 mnDecimalDigits = 0;
277 SetDecimalDigits( 0 );
278}
279
280LongCurrencyFormatter::LongCurrencyFormatter(Edit* pEdit)
281 : FormatterBase(pEdit)
282{
283 ImpInit();
284}
285
286LongCurrencyFormatter::~LongCurrencyFormatter()
287{
288}
289
290OUString const & LongCurrencyFormatter::GetCurrencySymbol() const
291{
293}
294
295void LongCurrencyFormatter::SetValue(const BigInt& rNewValue)
296{
297 SetUserValue(rNewValue);
298 SetEmptyFieldValueData( false );
299}
300
301void LongCurrencyFormatter::SetUserValue( BigInt nNewValue )
302{
303 if ( nNewValue > mnMax )
304 nNewValue = mnMax;
305 else if ( nNewValue < mnMin )
306 nNewValue = mnMin;
307 mnLastValue = nNewValue;
308
309 if ( !GetField() )
310 return;
311
312 OUString aStr = ImplGetCurr( GetLocaleDataWrapper(), nNewValue, GetDecimalDigits(), GetCurrencySymbol(), /*UseThousandSep*/true );
313 if ( GetField()->HasFocus() )
314 {
315 Selection aSelection = GetField()->GetSelection();
316 GetField()->SetText( aStr );
317 GetField()->SetSelection( aSelection );
318 }
319 else
320 GetField()->SetText( aStr );
321 MarkToBeReformatted( false );
322}
323
324BigInt LongCurrencyFormatter::GetValue() const
325{
326 if ( !GetField() )
327 return 0;
328
329 BigInt nTempValue;
330 if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue, GetDecimalDigits(), GetLocaleDataWrapper() ) )
331 {
332 if ( nTempValue > mnMax )
333 nTempValue = mnMax;
334 else if ( nTempValue < mnMin )
335 nTempValue = mnMin;
336 return nTempValue;
337 }
338 else
339 return mnLastValue;
340}
341
342void LongCurrencyFormatter::Reformat()
343{
344 if ( !GetField() )
345 return;
346
347 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
348 return;
349
350 OUString aStr;
351 bool bOK = ImplLongCurrencyReformat( GetField()->GetText(), mnMin, mnMax,
352 GetDecimalDigits(), GetLocaleDataWrapper(), aStr, *this );
353 if ( !bOK )
354 return;
355
356 if ( !aStr.isEmpty() )
357 {
358 GetField()->SetText( aStr );
359 MarkToBeReformatted( false );
360 ImplLongCurrencyGetValue( aStr, mnLastValue, GetDecimalDigits(), GetLocaleDataWrapper() );
361 }
362 else
363 SetValue( mnLastValue );
364}
365
366void LongCurrencyFormatter::ReformatAll()
367{
368 Reformat();
369}
370
371void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits )
372{
373 if ( nDigits > 9 )
374 nDigits = 9;
375
376 mnDecimalDigits = nDigits;
377 ReformatAll();
378}
379
380
381
382LongCurrencyBox::LongCurrencyBox(vcl::Window* pParent, WinBits nWinStyle)
383 : ComboBox(pParent, nWinStyle)
384 , LongCurrencyFormatter(this)
385{
386 Reformat();
387}
388
389bool LongCurrencyBox::EventNotify( NotifyEvent& rNEvt )
390{
391 if( rNEvt.GetType() == NotifyEventType::GETFOCUS )
392 {
393 MarkToBeReformatted( false );
394 }
395 else if( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
396 {
397 if ( MustBeReformatted() )
398 {
399 Reformat();
401 }
402 }
403 return ComboBox::EventNotify( rNEvt );
404}
405
406void LongCurrencyBox::Modify()
407{
408 MarkToBeReformatted( true );
410}
411
412void LongCurrencyBox::ReformatAll()
413{
414 OUString aStr;
415 SetUpdateMode( false );
416 const sal_Int32 nEntryCount = GetEntryCount();
417 for ( sal_Int32 i=0; i < nEntryCount; ++i )
418 {
419 ImplLongCurrencyReformat( GetEntry( i ), mnMin, mnMax,
420 GetDecimalDigits(), GetLocaleDataWrapper(),
421 aStr, *this );
422 RemoveEntryAt(i);
423 InsertEntry( aStr, i );
424 }
425 LongCurrencyFormatter::Reformat();
426 SetUpdateMode( true );
427}
428
429/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const LocaleDataWrapper & GetLocaleDataWrapper() const
static const AllSettings & GetSettings()
Gets the application's settings.
Definition: svapp.cxx:638
bool IsZero() const
bool IsNeg() const
A widget used to choose from a list of items and which has an entry.
Definition: combobox.hxx:39
virtual void Modify() override
Definition: combobox.cxx:821
virtual bool EventNotify(NotifyEvent &rNEvt) override
Definition: combobox.cxx:716
Definition: edit.hxx:56
sal_uInt16 getCurrNegativeFormat() const
OUString getCurr(sal_Int64 nNumber, sal_uInt16 nDecimals, std::u16string_view rCurrencySymbol, bool bUseThousandSep=true) const
OUString getNum(sal_Int64 nNumber, sal_uInt16 nDecimals, bool bUseThousandSep=true, bool bTrailingZeros=true) const
const OUString & getCurrSymbol() const
const OUString & getNumDecimalSepAlt() const
const OUString & getNumDecimalSep() const
NotifyEventType GetType() const
Definition: event.hxx:308
static unsigned int Power10(unsigned int n)
Definition: builder.cxx:267
Any value
virtual void SetValue(tools::Long nNew) override
static bool ImplCurrencyGetValue(const OUString &rStr, sal_Int64 &rValue, sal_uInt16 nDecDigits, const LocaleDataWrapper &rWrapper)
Definition: field.cxx:1652
sal_Int16 nValue
TriState
TRISTATE_FALSE
TRISTATE_TRUE
sal_Int64 n
#define SAL_WARN_IF(condition, area, stream)
static bool ImplLongCurrencyGetValue(const OUString &rStr, BigInt &rValue, sal_uInt16 nDecDigits, const LocaleDataWrapper &rLocaleDataWrapper)
Definition: longcurr.cxx:217
bool ImplLongCurrencyReformat(const OUString &rStr, BigInt const &nMin, BigInt const &nMax, sal_uInt16 nDecDigits, const LocaleDataWrapper &rLocaleDataWrapper, OUString &rOutStr, LongCurrencyFormatter const &rFormatter)
Definition: longcurr.cxx:249
aStr
int i
const LocaleDataWrapper & GetLocaleDataWrapper(LanguageType nLang)
long Long
IMPL_LINK_NOARG(HexColorControl, OnAsyncModifyHdl, void *, void)
IMPL_LINK(CustomWeld, DoResize, const Size &, rSize, void)
Definition: customweld.cxx:46
const char GetValue[]
Any result
sal_Int64 WinBits
Definition: wintypes.hxx:109