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/longcurr.hxx>
30 
32 
33 using namespace ::comphelper;
34 
35 namespace
36 {
37 
38 BigInt ImplPower10( sal_uInt16 n )
39 {
40  sal_uInt16 i;
41  BigInt nValue = 1;
42 
43  for ( i=0; i < n; i++ )
44  nValue *= 10;
45 
46  return nValue;
47 }
48 
49 OUString ImplGetCurr( const LocaleDataWrapper& rLocaleDataWrapper, const BigInt &rNumber, sal_uInt16 nDigits, const OUString& rCurrSymbol, bool bShowThousandSep )
50 {
51  SAL_WARN_IF( nDigits >= 10, "vcl", "LongCurrency may only have 9 decimal places" );
52 
53  if ( rNumber.IsZero() || static_cast<long>(rNumber) )
54  return rLocaleDataWrapper.getCurr( static_cast<long>(rNumber), nDigits, rCurrSymbol, bShowThousandSep );
55 
56  BigInt aTmp( ImplPower10( nDigits ) );
57  BigInt aInteger( rNumber );
58  aInteger.Abs();
59  aInteger /= aTmp;
60  BigInt aFraction( rNumber );
61  aFraction.Abs();
62  aFraction %= aTmp;
63  if ( !aInteger.IsZero() )
64  {
65  aFraction += aTmp;
66  aTmp = 1000000000;
67  }
68  if ( rNumber.IsNeg() )
69  aFraction *= -1;
70 
71  OUStringBuffer aTemplate = rLocaleDataWrapper.getCurr( static_cast<long>(aFraction), nDigits, rCurrSymbol, bShowThousandSep );
72  while( !aInteger.IsZero() )
73  {
74  aFraction = aInteger;
75  aFraction %= aTmp;
76  aInteger /= aTmp;
77  if( !aInteger.IsZero() )
78  aFraction += aTmp;
79 
80  OUString aFractionStr = rLocaleDataWrapper.getNum( static_cast<long>(aFraction), 0 );
81 
82  sal_Int32 nSPos = aTemplate.indexOf( '1' );
83  if (nSPos == -1)
84  break;
85  if ( aFractionStr.getLength() == 1 )
86  aTemplate[ nSPos ] = aFractionStr[0];
87  else
88  {
89  aTemplate.remove( nSPos, 1 );
90  aTemplate.insert( nSPos, aFractionStr );
91  }
92  }
93 
94  return aTemplate.makeStringAndClear();
95 }
96 
97 bool ImplCurrencyGetValue( const OUString& rStr, BigInt& rValue,
98  sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper )
99 {
100  OUString aStr = rStr;
101  OUStringBuffer aStr1;
102  OUStringBuffer aStr2;
103  sal_Int32 nDecPos;
104  bool bNegative = false;
105 
106  // On empty string
107  if ( rStr.isEmpty() )
108  return false;
109 
110  // Trim leading and trailing spaces
111  aStr = string::strip(aStr, ' ');
112 
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() );
117 
118  if ( nDecPos != -1 )
119  {
120  aStr1 = aStr.copy( 0, nDecPos );
121  aStr2.append(std::u16string_view(aStr).substr(nDecPos+1));
122  }
123  else
124  aStr1 = aStr;
125 
126  // Negative?
127  if ( (aStr[ 0 ] == '(') && (aStr[ aStr.getLength()-1 ] == ')') )
128  bNegative = true;
129  if ( !bNegative )
130  {
131  for (sal_Int32 i=0; i < aStr.getLength(); i++ )
132  {
133  if ( (aStr[ i ] >= '0') && (aStr[ i ] <= '9') )
134  break;
135  else if ( aStr[ i ] == '-' )
136  {
137  bNegative = true;
138  break;
139  }
140  }
141  }
142  if ( !bNegative && !aStr.isEmpty() )
143  {
144  sal_uInt16 nFormat = rLocaleDataWrapper.getCurrNegativeFormat();
145  if ( (nFormat == 3) || (nFormat == 6) ||
146  (nFormat == 7) || (nFormat == 10) )
147  {
148  for (sal_Int32 i = aStr.getLength()-1; i > 0; i++ )
149  {
150  if ( (aStr[ i ] >= '0') && (aStr[ i ] <= '9') )
151  break;
152  else if ( aStr[ i ] == '-' )
153  {
154  bNegative = true;
155  break;
156  }
157  }
158  }
159  }
160 
161  // delete unwanted characters
162  for (sal_Int32 i=0; i < aStr1.getLength(); )
163  {
164  if ( (aStr1[ i ] >= '0') && (aStr1[ i ] <= '9') )
165  i++;
166  else
167  aStr1.remove( i, 1 );
168  }
169  for (sal_Int32 i=0; i < aStr2.getLength(); )
170  {
171  if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
172  ++i;
173  else
174  aStr2.remove(i, 1);
175  }
176 
177  if ( aStr1.isEmpty() && aStr2.isEmpty())
178  return false;
179 
180  if ( aStr1.isEmpty() )
181  aStr1 = "0";
182  if ( bNegative )
183  aStr1.insert( 0, '-');
184 
185  // Cut down decimal part and round while doing so
186  bool bRound = false;
187  if (aStr2.getLength() > nDecDigits)
188  {
189  if (aStr2[nDecDigits] >= '5')
190  bRound = true;
191  string::truncateToLength(aStr2, nDecDigits);
192  }
193  if (aStr2.getLength() < nDecDigits)
194  string::padToLength(aStr2, nDecDigits, '0');
195 
196  aStr1.append(aStr2);
197  aStr = aStr1.makeStringAndClear();
198 
199  // check range
200  BigInt nValue( aStr );
201  if ( bRound )
202  {
203  if ( !bNegative )
204  nValue+=1;
205  else
206  nValue-=1;
207  }
208 
209  rValue = nValue;
210 
211  return true;
212 }
213 
214 } // namespace
215 
216 static bool ImplLongCurrencyGetValue( const OUString& rStr, BigInt& rValue,
217  sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper )
218 {
219  return ImplCurrencyGetValue( rStr, rValue, nDecDigits, rLocaleDataWrapper );
220 }
221 
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 )
226 {
227  BigInt nValue;
228  if ( !ImplCurrencyGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper ) )
229  return true;
230  else
231  {
232  BigInt nTempVal = nValue;
233  if ( nTempVal > nMax )
234  nTempVal = nMax;
235  else if ( nTempVal < nMin )
236  nTempVal = nMin;
237 
238  rOutStr = ImplGetCurr( rLocaleDataWrapper, nTempVal, nDecDigits, rFormatter.GetCurrencySymbol(), rFormatter.IsUseThousandSep() );
239  return true;
240  }
241 }
242 
244 {
245  mnLastValue = 0;
246  mnMin = 0;
247  mnMax = 0x7FFFFFFF;
248  mnMax *= 0x7FFFFFFF;
249  mnDecimalDigits = 0;
250  mbThousandSep = true;
251  SetDecimalDigits( 0 );
252 }
253 
255  : FormatterBase(pEdit)
256 {
257  ImpInit();
258 }
259 
261 {
262 }
263 
264 void LongCurrencyFormatter::SetCurrencySymbol( const OUString& rStr )
265 {
266  maCurrencySymbol= rStr;
267  ReformatAll();
268 }
269 
271 {
273 }
274 
276 {
277  SetUserValue(rNewValue);
278  SetEmptyFieldValueData( false );
279 }
280 
282 {
283  if ( nNewValue > mnMax )
284  nNewValue = mnMax;
285  else if ( nNewValue < mnMin )
286  nNewValue = mnMin;
287  mnLastValue = nNewValue;
288 
289  if ( !GetField() )
290  return;
291 
292  OUString aStr = ImplGetCurr( GetLocaleDataWrapper(), nNewValue, GetDecimalDigits(), GetCurrencySymbol(), IsUseThousandSep() );
293  if ( GetField()->HasFocus() )
294  {
295  Selection aSelection = GetField()->GetSelection();
296  GetField()->SetText( aStr );
297  GetField()->SetSelection( aSelection );
298  }
299  else
300  GetField()->SetText( aStr );
301  MarkToBeReformatted( false );
302 }
303 
305 {
306  if ( !GetField() )
307  return 0;
308 
309  BigInt nTempValue;
310  if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue, GetDecimalDigits(), GetLocaleDataWrapper() ) )
311  {
312  if ( nTempValue > mnMax )
313  nTempValue = mnMax;
314  else if ( nTempValue < mnMin )
315  nTempValue = mnMin;
316  return nTempValue;
317  }
318  else
319  return mnLastValue;
320 }
321 
323 {
324  if ( !GetField() )
325  return;
326 
327  if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
328  return;
329 
330  OUString aStr;
331  bool bOK = ImplLongCurrencyReformat( GetField()->GetText(), mnMin, mnMax,
332  GetDecimalDigits(), GetLocaleDataWrapper(), aStr, *this );
333  if ( !bOK )
334  return;
335 
336  if ( !aStr.isEmpty() )
337  {
338  GetField()->SetText( aStr );
339  MarkToBeReformatted( false );
341  }
342  else
344 }
345 
347 {
348  Reformat();
349 }
350 
352 {
353  mnMin = rNewMin;
354  ReformatAll();
355 }
356 
358 {
359  mnMax = rNewMax;
360  ReformatAll();
361 }
362 
364 {
365  mbThousandSep = b;
366  ReformatAll();
367 }
368 
369 void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits )
370 {
371  if ( nDigits > 9 )
372  nDigits = 9;
373 
374  mnDecimalDigits = nDigits;
375  ReformatAll();
376 }
377 
378 
380 {
381  Selection aSelect = pField->GetSelection();
382  aSelect.Justify();
383  OUString aText = pField->GetText();
384  bool bLastSelected = aSelect.Max() == aText.getLength();
385 
386  BigInt nOldLastValue = pField->mnLastValue;
387  pField->SetUserValue(rNewValue);
388  pField->mnLastValue = nOldLastValue;
389 
390  if ( bLastSelected )
391  {
392  if ( !aSelect.Len() )
393  aSelect.Min() = SELECTION_MAX;
394  aSelect.Max() = SELECTION_MAX;
395  }
396  pField->SetSelection( aSelect );
397  pField->SetModifyFlag();
398  pField->Modify();
399 }
400 
402  : SpinField( pParent, nWinStyle )
403  , LongCurrencyFormatter(this)
404 {
405  mnSpinSize = 1;
406  mnFirst = mnMin;
407  mnLast = mnMax;
408 
409  Reformat();
410 }
411 
413 {
414  if( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
415  {
416  MarkToBeReformatted( false );
417  }
418  else if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
419  {
420  if ( MustBeReformatted() )
421  {
422  Reformat();
424  }
425  }
426  return SpinField::EventNotify( rNEvt );
427 }
428 
430 {
431  MarkToBeReformatted( true );
433 }
434 
436 {
437  BigInt nValue = GetValue();
438  nValue += mnSpinSize;
439  if ( nValue > mnMax )
440  nValue = mnMax;
441 
442  ImplNewLongCurrencyFieldValue( this, nValue );
443  SpinField::Up();
444 }
445 
447 {
448  BigInt nValue = GetValue();
449  nValue -= mnSpinSize;
450  if ( nValue < mnMin )
451  nValue = mnMin;
452 
453  ImplNewLongCurrencyFieldValue( this, nValue );
454  SpinField::Down();
455 }
456 
458 {
461 }
462 
464 {
466  SpinField::Last();
467 }
468 
470  : ComboBox(pParent, nWinStyle)
471  , LongCurrencyFormatter(this)
472 {
473  Reformat();
474 }
475 
477 {
478  if( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
479  {
480  MarkToBeReformatted( false );
481  }
482  else if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
483  {
484  if ( MustBeReformatted() )
485  {
486  Reformat();
488  }
489  }
490  return ComboBox::EventNotify( rNEvt );
491 }
492 
494 {
495  MarkToBeReformatted( true );
497 }
498 
500 {
501  OUString aStr;
502  SetUpdateMode( false );
503  const sal_Int32 nEntryCount = GetEntryCount();
504  for ( sal_Int32 i=0; i < nEntryCount; ++i )
505  {
508  aStr, *this );
509  RemoveEntryAt(i);
510  InsertEntry( aStr, i );
511  }
513  SetUpdateMode( true );
514 }
515 
516 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
LongCurrencyField(vcl::Window *pParent, WinBits nWinStyle)
Definition: longcurr.cxx:401
sal_uInt16 getCurrNegativeFormat() const
OUString maCurrencySymbol
Definition: longcurr.hxx:65
void ImplNewLongCurrencyFieldValue(LongCurrencyField *pField, const BigInt &rNewValue)
Definition: longcurr.cxx:379
void ReformatAll() override
Definition: longcurr.cxx:499
void Up() override
Definition: longcurr.cxx:435
void Modify() override
Definition: longcurr.cxx:429
void SetCurrencySymbol(const OUString &rStr)
Definition: longcurr.cxx:264
long Len() const
virtual void Up()
Definition: spinfld.cxx:359
bool MustBeReformatted() const
Definition: field.hxx:67
void SetMin(const BigInt &rNewMin)
Definition: longcurr.cxx:351
SAL_DLLPRIVATE void ImpInit()
Definition: longcurr.cxx:243
OUString GetEntry(sal_Int32 nPos) const
Definition: combobox.cxx:945
friend void ImplNewLongCurrencyFieldValue(LongCurrencyField *, const BigInt &)
Definition: longcurr.cxx:379
sal_Int64 WinBits
void SetMax(const BigInt &rNewMax)
Definition: longcurr.cxx:357
Edit * GetField() const
Definition: field.hxx:58
void Last() override
Definition: longcurr.cxx:463
void SetUserValue(BigInt nNewValue)
Definition: longcurr.cxx:281
OUString const & GetCurrencySymbol() const
Definition: longcurr.cxx:270
bool IsUseThousandSep() const
Definition: longcurr.hxx:39
virtual void SetModifyFlag()
Definition: edit.cxx:2588
virtual const Selection & GetSelection() const
Definition: edit.cxx:2468
Definition: edit.hxx:57
virtual void Down()
Definition: spinfld.cxx:364
virtual void SetSelection(const Selection &rSelection)
Definition: edit.cxx:2394
static bool ImplCurrencyGetValue(const OUString &rStr, sal_Int64 &rValue, sal_uInt16 nDecDigits, const LocaleDataWrapper &rWrapper)
Definition: field.cxx:1865
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:222
sal_uInt16 mnDecimalDigits
Definition: longcurr.hxx:66
void Down() override
Definition: longcurr.cxx:446
const OUString & getNumDecimalSep() const
void First() override
Definition: longcurr.cxx:457
long Min() const
virtual bool EventNotify(NotifyEvent &rNEvt) override
Definition: spinfld.cxx:490
void Justify()
virtual bool EventNotify(NotifyEvent &rNEvt) override
Definition: combobox.cxx:702
int i
void SetEmptyFieldValueData(bool bValue)
Definition: field.hxx:54
friend bool ImplLongCurrencyReformat(const OUString &, BigInt const &, BigInt const &, sal_uInt16, const LocaleDataWrapper &, OUString &, LongCurrencyFormatter const &)
Definition: longcurr.cxx:222
void SetUpdateMode(bool bUpdate)
Definition: window.cxx:2971
virtual bool EventNotify(NotifyEvent &rNEvt) override
Definition: longcurr.cxx:476
virtual OUString GetText() const override
Definition: edit.cxx:2564
virtual void Modify()
Definition: edit.cxx:2305
bool IsZero() const
void RemoveEntryAt(sal_Int32 nPos)
Definition: combobox.cxx:914
MouseNotifyEvent GetType() const
Definition: event.hxx:294
OUString getCurr(sal_Int64 nNumber, sal_uInt16 nDecimals, const OUString &rCurrencySymbol, bool bUseThousandSep=true) const
BigInt GetValue() const
Definition: longcurr.cxx:304
bool IsNeg() const
SAL_DLLPRIVATE bool ImplGetEmptyFieldValue() const
Definition: field.hxx:52
virtual bool EventNotify(NotifyEvent &rNEvt) override
Definition: longcurr.cxx:412
const OUString & getCurrSymbol() const
const OUString & getNumDecimalSepAlt() const
void SetDecimalDigits(sal_uInt16 nDigits)
Definition: longcurr.cxx:369
void MarkToBeReformatted(bool b)
Definition: field.hxx:68
virtual void ReformatAll() override
Definition: longcurr.cxx:346
virtual void First()
Definition: spinfld.cxx:369
sal_Int32 GetEntryCount() const
Definition: combobox.cxx:954
#define SAL_WARN_IF(condition, area, stream)
void Modify() override
Definition: longcurr.cxx:493
void SetValue(const BigInt &rNewValue)
Definition: longcurr.cxx:275
virtual void Last()
Definition: spinfld.cxx:374
virtual void Reformat() override
Definition: longcurr.cxx:322
sal_uInt16 GetDecimalDigits() const
Definition: longcurr.hxx:50
A widget used to choose from a list of items and which has an entry.
Definition: combobox.hxx:34
#define SELECTION_MAX
OUString getNum(sal_Int64 nNumber, sal_uInt16 nDecimals, bool bUseThousandSep=true, bool bTrailingZeros=true) const
long Max() const
sal_Int32 InsertEntry(const OUString &rStr, sal_Int32 nPos=COMBOBOX_APPEND)
Definition: combobox.cxx:868
virtual void SetText(const OUString &rStr) override
Definition: edit.cxx:2545
LongCurrencyBox(vcl::Window *pParent, WinBits nWinStyle)
Definition: longcurr.cxx:469
const LocaleDataWrapper & GetLocaleDataWrapper() const
Definition: field.cxx:457
aStr
LongCurrencyFormatter(Edit *pEdit)
Definition: longcurr.cxx:254
static bool ImplLongCurrencyGetValue(const OUString &rStr, BigInt &rValue, sal_uInt16 nDecDigits, const LocaleDataWrapper &rLocaleDataWrapper)
Definition: longcurr.cxx:216
virtual ~LongCurrencyFormatter() override
Definition: longcurr.cxx:260
void SetUseThousandSep(bool b)
Definition: longcurr.cxx:363
virtual void Modify() override
Definition: combobox.cxx:807