Line data Source code
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 <comphelper/string.hxx>
21 : #include <sot/object.hxx>
22 : #include <sot/factory.hxx>
23 : #include <tools/debug.hxx>
24 : #include <tools/bigint.hxx>
25 :
26 : #include <tools/rc.h>
27 :
28 : #include <vcl/event.hxx>
29 : #include <vcl/svapp.hxx>
30 : #include <vcl/longcurr.hxx>
31 :
32 : #include <svdata.hxx>
33 :
34 : #include <unotools/localedatawrapper.hxx>
35 :
36 : using namespace ::comphelper;
37 :
38 : #define FORMAT_LONGCURRENCY 4
39 :
40 0 : static BigInt ImplPower10( sal_uInt16 n )
41 : {
42 : sal_uInt16 i;
43 0 : BigInt nValue = 1;
44 :
45 0 : for ( i=0; i < n; i++ )
46 0 : nValue *= 10;
47 :
48 0 : return nValue;
49 : }
50 :
51 0 : static XubString ImplGetCurr( const LocaleDataWrapper& rLocaleDataWrapper, const BigInt &rNumber, sal_uInt16 nDigits, const String& rCurrSymbol, sal_Bool bShowThousandSep )
52 : {
53 : DBG_ASSERT( nDigits < 10, "LongCurrency may only have 9 decimal places" );
54 :
55 0 : if ( rNumber.IsZero() || (long)rNumber )
56 0 : return rLocaleDataWrapper.getCurr( (long)rNumber, nDigits, rCurrSymbol, bShowThousandSep );
57 :
58 0 : BigInt aTmp( ImplPower10( nDigits ) );
59 0 : BigInt aInteger( rNumber );
60 0 : aInteger.Abs();
61 0 : aInteger /= aTmp;
62 0 : BigInt aFraction( rNumber );
63 0 : aFraction.Abs();
64 0 : aFraction %= aTmp;
65 0 : if ( !aInteger.IsZero() )
66 : {
67 0 : aFraction += aTmp;
68 0 : aTmp = 1000000000L;
69 : }
70 0 : if ( rNumber.IsNeg() )
71 0 : aFraction *= -1;
72 :
73 0 : XubString aTemplate = rLocaleDataWrapper.getCurr( (long)aFraction, nDigits, rCurrSymbol, bShowThousandSep );
74 0 : while( !aInteger.IsZero() )
75 : {
76 0 : aFraction = aInteger;
77 0 : aFraction %= aTmp;
78 0 : aInteger /= aTmp;
79 0 : if( !aInteger.IsZero() )
80 0 : aFraction += aTmp;
81 :
82 0 : XubString aFractionStr = rLocaleDataWrapper.getNum( (long)aFraction, 0 );
83 :
84 0 : xub_StrLen nSPos = aTemplate.Search( '1' );
85 0 : if ( aFractionStr.Len() == 1 )
86 0 : aTemplate.SetChar( nSPos, aFractionStr.GetChar( 0 ) );
87 : else
88 : {
89 0 : aTemplate.Erase( nSPos, 1 );
90 0 : aTemplate.Insert( aFractionStr, nSPos );
91 : }
92 0 : }
93 :
94 0 : return aTemplate;
95 : }
96 :
97 0 : static bool ImplNumericProcessKeyInput( Edit*, const KeyEvent& rKEvt,
98 : sal_Bool bStrictFormat, sal_Bool bThousandSep,
99 : const LocaleDataWrapper& rLocaleDataWrapper )
100 : {
101 0 : if ( !bStrictFormat )
102 0 : return false;
103 : else
104 : {
105 0 : sal_Unicode cChar = rKEvt.GetCharCode();
106 0 : sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
107 :
108 0 : if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
109 0 : (nGroup == KEYGROUP_MISC) ||
110 0 : ((cChar >= '0') && (cChar <= '9')) ||
111 0 : (bThousandSep && string::equals(rLocaleDataWrapper.getNumThousandSep(), cChar)) ||
112 0 : (string::equals(rLocaleDataWrapper.getNumDecimalSep(), cChar) ) ||
113 : (cChar == '-') )
114 0 : return false;
115 : else
116 0 : return true;
117 : }
118 : }
119 :
120 0 : static bool ImplNumericGetValue( const XubString& rStr, BigInt& rValue,
121 : sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper,
122 : sal_Bool bCurrency = sal_False )
123 : {
124 0 : XubString aStr = rStr;
125 0 : XubString aStr1;
126 0 : OUStringBuffer aStr2;
127 : sal_uInt16 nDecPos;
128 0 : sal_Bool bNegative = sal_False;
129 :
130 : // On empty string
131 0 : if ( !rStr.Len() )
132 0 : return false;
133 :
134 : // Trim leading and trailing spaces
135 0 : aStr = string::strip(aStr, ' ');
136 :
137 : // Find decimal sign's position
138 0 : nDecPos = aStr.Search( rLocaleDataWrapper.getNumDecimalSep() );
139 :
140 0 : if ( nDecPos != STRING_NOTFOUND )
141 : {
142 0 : aStr1 = aStr.Copy( 0, nDecPos );
143 0 : aStr2.append(aStr.Copy(nDecPos+1));
144 : }
145 : else
146 0 : aStr1 = aStr;
147 :
148 : // Negative?
149 0 : if ( bCurrency )
150 : {
151 0 : if ( (aStr.GetChar( 0 ) == '(') && (aStr.GetChar( aStr.Len()-1 ) == ')') )
152 0 : bNegative = sal_True;
153 0 : if ( !bNegative )
154 : {
155 0 : for (xub_StrLen i=0; i < aStr.Len(); i++ )
156 : {
157 0 : if ( (aStr.GetChar( i ) >= '0') && (aStr.GetChar( i ) <= '9') )
158 0 : break;
159 0 : else if ( aStr.GetChar( i ) == '-' )
160 : {
161 0 : bNegative = sal_True;
162 0 : break;
163 : }
164 : }
165 : }
166 0 : if ( !bNegative && bCurrency && aStr.Len() )
167 : {
168 0 : sal_uInt16 nFormat = rLocaleDataWrapper.getCurrNegativeFormat();
169 0 : if ( (nFormat == 3) || (nFormat == 6) ||
170 0 : (nFormat == 7) || (nFormat == 10) )
171 : {
172 0 : for (xub_StrLen i = (sal_uInt16)(aStr.Len()-1); i > 0; i++ )
173 : {
174 0 : if ( (aStr.GetChar( i ) >= '0') && (aStr.GetChar( i ) <= '9') )
175 0 : break;
176 0 : else if ( aStr.GetChar( i ) == '-' )
177 : {
178 0 : bNegative = sal_True;
179 0 : break;
180 : }
181 : }
182 : }
183 : }
184 : }
185 : else
186 : {
187 0 : if ( aStr1.GetChar( 0 ) == '-' )
188 0 : bNegative = sal_True;
189 : }
190 :
191 : // delete unwanted characters
192 0 : for (xub_StrLen i=0; i < aStr1.Len(); )
193 : {
194 0 : if ( (aStr1.GetChar( i ) >= '0') && (aStr1.GetChar( i ) <= '9') )
195 0 : i++;
196 : else
197 0 : aStr1.Erase( i, 1 );
198 : }
199 0 : for (sal_Int32 i=0; i < aStr2.getLength();)
200 : {
201 0 : if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
202 0 : ++i;
203 : else
204 0 : aStr2.remove(i, 1);
205 : }
206 :
207 0 : if (!aStr1.Len() && aStr2.isEmpty())
208 0 : return false;
209 :
210 0 : if ( !aStr1.Len() )
211 0 : aStr1.Insert( '0' );
212 0 : if ( bNegative )
213 0 : aStr1.Insert( '-', 0 );
214 :
215 : // Cut down decimal part and round while doing so
216 0 : bool bRound = false;
217 0 : if (aStr2.getLength() > nDecDigits)
218 : {
219 0 : if (aStr2[nDecDigits] >= '5')
220 0 : bRound = true;
221 0 : string::truncateToLength(aStr2, nDecDigits);
222 : }
223 0 : if (aStr2.getLength() < nDecDigits)
224 0 : string::padToLength(aStr2, nDecDigits, '0');
225 :
226 0 : aStr = aStr1;
227 0 : aStr += aStr2.makeStringAndClear();
228 :
229 : // check range
230 0 : BigInt nValue( aStr );
231 0 : if ( bRound )
232 : {
233 0 : if ( !bNegative )
234 0 : nValue+=1;
235 : else
236 0 : nValue-=1;
237 : }
238 :
239 0 : rValue = nValue;
240 :
241 0 : return true;
242 : }
243 :
244 0 : static bool ImplLongCurrencyProcessKeyInput( Edit* pEdit, const KeyEvent& rKEvt,
245 : sal_Bool, sal_Bool bUseThousandSep, const LocaleDataWrapper& rLocaleDataWrapper )
246 : {
247 : // There's no StrictFormat that makes sense here, thus allow all chars
248 0 : return ImplNumericProcessKeyInput( pEdit, rKEvt, sal_False, bUseThousandSep, rLocaleDataWrapper );
249 : }
250 :
251 0 : inline bool ImplLongCurrencyGetValue( const XubString& rStr, BigInt& rValue,
252 : sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper )
253 : {
254 0 : return ImplNumericGetValue( rStr, rValue, nDecDigits, rLocaleDataWrapper, sal_True );
255 : }
256 :
257 0 : bool ImplLongCurrencyReformat( const XubString& rStr, BigInt nMin, BigInt nMax,
258 : sal_uInt16 nDecDigits,
259 : const LocaleDataWrapper& rLocaleDataWrapper, String& rOutStr,
260 : LongCurrencyFormatter& rFormatter )
261 : {
262 0 : BigInt nValue;
263 0 : if ( !ImplNumericGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper, sal_True ) )
264 0 : return true;
265 : else
266 : {
267 0 : BigInt nTempVal = nValue;
268 0 : if ( nTempVal > nMax )
269 0 : nTempVal = nMax;
270 0 : else if ( nTempVal < nMin )
271 0 : nTempVal = nMin;
272 :
273 0 : if ( rFormatter.GetErrorHdl().IsSet() && (nValue != nTempVal) )
274 : {
275 0 : rFormatter.mnCorrectedValue = nTempVal;
276 0 : if ( !rFormatter.GetErrorHdl().Call( &rFormatter ) )
277 : {
278 0 : rFormatter.mnCorrectedValue = 0;
279 0 : return false;
280 : }
281 : else
282 : {
283 0 : rFormatter.mnCorrectedValue = 0;
284 : }
285 : }
286 :
287 0 : rOutStr = ImplGetCurr( rLocaleDataWrapper, nTempVal, nDecDigits, rFormatter.GetCurrencySymbol(), rFormatter.IsUseThousandSep() );
288 0 : return true;
289 : }
290 : }
291 :
292 0 : void LongCurrencyFormatter::ImpInit()
293 : {
294 0 : mnFieldValue = 0;
295 0 : mnLastValue = 0;
296 0 : mnMin = 0;
297 0 : mnMax = 0x7FFFFFFF;
298 0 : mnMax *= 0x7FFFFFFF;
299 0 : mnCorrectedValue = 0;
300 0 : mnDecimalDigits = 0;
301 0 : mnType = FORMAT_LONGCURRENCY;
302 0 : mbThousandSep = sal_True;
303 0 : SetDecimalDigits( 0 );
304 0 : }
305 :
306 0 : LongCurrencyFormatter::LongCurrencyFormatter()
307 : {
308 0 : ImpInit();
309 0 : }
310 :
311 0 : LongCurrencyFormatter::~LongCurrencyFormatter()
312 : {
313 0 : }
314 :
315 0 : void LongCurrencyFormatter::SetCurrencySymbol( const String& rStr )
316 : {
317 0 : maCurrencySymbol= rStr;
318 0 : ReformatAll();
319 0 : }
320 :
321 0 : String LongCurrencyFormatter::GetCurrencySymbol() const
322 : {
323 0 : return !maCurrencySymbol.isEmpty() ? maCurrencySymbol : GetLocaleDataWrapper().getCurrSymbol();
324 : }
325 :
326 0 : void LongCurrencyFormatter::SetValue( BigInt nNewValue )
327 : {
328 0 : SetUserValue( nNewValue );
329 0 : mnFieldValue = mnLastValue;
330 0 : SetEmptyFieldValueData( sal_False );
331 0 : }
332 :
333 0 : void LongCurrencyFormatter::SetUserValue( BigInt nNewValue )
334 : {
335 0 : if ( nNewValue > mnMax )
336 0 : nNewValue = mnMax;
337 0 : else if ( nNewValue < mnMin )
338 0 : nNewValue = mnMin;
339 0 : mnLastValue = nNewValue;
340 :
341 0 : if ( !GetField() )
342 0 : return;
343 :
344 0 : XubString aStr = ImplGetCurr( GetLocaleDataWrapper(), nNewValue, GetDecimalDigits(), GetCurrencySymbol(), IsUseThousandSep() );
345 0 : if ( GetField()->HasFocus() )
346 : {
347 0 : Selection aSelection = GetField()->GetSelection();
348 0 : GetField()->SetText( aStr );
349 0 : GetField()->SetSelection( aSelection );
350 : }
351 : else
352 0 : GetField()->SetText( aStr );
353 0 : MarkToBeReformatted( sal_False );
354 : }
355 :
356 0 : BigInt LongCurrencyFormatter::GetValue() const
357 : {
358 0 : if ( !GetField() )
359 0 : return 0;
360 :
361 0 : BigInt nTempValue;
362 0 : if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue, GetDecimalDigits(), GetLocaleDataWrapper() ) )
363 : {
364 0 : if ( nTempValue > mnMax )
365 0 : nTempValue = mnMax;
366 0 : else if ( nTempValue < mnMin )
367 0 : nTempValue = mnMin;
368 0 : return nTempValue;
369 : }
370 : else
371 0 : return mnLastValue;
372 : }
373 :
374 0 : void LongCurrencyFormatter::Reformat()
375 : {
376 0 : if ( !GetField() )
377 0 : return;
378 :
379 0 : if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
380 0 : return;
381 :
382 0 : XubString aStr;
383 0 : bool bOK = ImplLongCurrencyReformat( GetField()->GetText(), mnMin, mnMax,
384 0 : GetDecimalDigits(), GetLocaleDataWrapper(), aStr, *this );
385 0 : if ( !bOK )
386 0 : return;
387 :
388 0 : if ( aStr.Len() )
389 : {
390 0 : GetField()->SetText( aStr );
391 0 : MarkToBeReformatted( sal_False );
392 0 : ImplLongCurrencyGetValue( aStr, mnLastValue, GetDecimalDigits(), GetLocaleDataWrapper() );
393 : }
394 : else
395 0 : SetValue( mnLastValue );
396 : }
397 :
398 0 : void LongCurrencyFormatter::ReformatAll()
399 : {
400 0 : Reformat();
401 0 : }
402 :
403 0 : void LongCurrencyFormatter::SetMin( BigInt nNewMin )
404 : {
405 0 : mnMin = nNewMin;
406 0 : ReformatAll();
407 0 : }
408 :
409 0 : void LongCurrencyFormatter::SetMax( BigInt nNewMax )
410 : {
411 0 : mnMax = nNewMax;
412 0 : ReformatAll();
413 0 : }
414 :
415 0 : void LongCurrencyFormatter::SetUseThousandSep( sal_Bool b )
416 : {
417 0 : mbThousandSep = b;
418 0 : ReformatAll();
419 0 : }
420 :
421 0 : void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits )
422 : {
423 0 : if ( nDigits > 9 )
424 0 : nDigits = 9;
425 :
426 0 : mnDecimalDigits = nDigits;
427 0 : ReformatAll();
428 0 : }
429 :
430 0 : sal_uInt16 LongCurrencyFormatter::GetDecimalDigits() const
431 : {
432 0 : return mnDecimalDigits;
433 : }
434 :
435 0 : void ImplNewLongCurrencyFieldValue( LongCurrencyField* pField, BigInt nNewValue )
436 : {
437 0 : Selection aSelect = pField->GetSelection();
438 0 : aSelect.Justify();
439 0 : XubString aText = pField->GetText();
440 0 : bool bLastSelected = ((xub_StrLen)aSelect.Max() == aText.Len());
441 :
442 0 : BigInt nOldLastValue = pField->mnLastValue;
443 0 : pField->SetUserValue( nNewValue );
444 0 : pField->mnLastValue = nOldLastValue;
445 :
446 0 : if ( bLastSelected )
447 : {
448 0 : if ( !aSelect.Len() )
449 0 : aSelect.Min() = SELECTION_MAX;
450 0 : aSelect.Max() = SELECTION_MAX;
451 : }
452 0 : pField->SetSelection( aSelect );
453 0 : pField->SetModifyFlag();
454 0 : pField->Modify();
455 0 : }
456 :
457 0 : LongCurrencyField::LongCurrencyField( Window* pParent, WinBits nWinStyle ) :
458 0 : SpinField( pParent, nWinStyle )
459 : {
460 0 : SetField( this );
461 0 : mnSpinSize = 1;
462 0 : mnFirst = mnMin;
463 0 : mnLast = mnMax;
464 :
465 0 : Reformat();
466 0 : }
467 :
468 :
469 0 : LongCurrencyField::~LongCurrencyField()
470 : {
471 0 : }
472 :
473 :
474 0 : long LongCurrencyField::PreNotify( NotifyEvent& rNEvt )
475 : {
476 0 : if( rNEvt.GetType() == EVENT_KEYINPUT )
477 : {
478 0 : if ( ImplLongCurrencyProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), GetLocaleDataWrapper() ) )
479 0 : return 1;
480 : }
481 0 : return SpinField::PreNotify( rNEvt );
482 : }
483 :
484 :
485 0 : long LongCurrencyField::Notify( NotifyEvent& rNEvt )
486 : {
487 0 : if( rNEvt.GetType() == EVENT_GETFOCUS )
488 : {
489 0 : MarkToBeReformatted( sal_False );
490 : }
491 0 : else if( rNEvt.GetType() == EVENT_LOSEFOCUS )
492 : {
493 0 : if ( MustBeReformatted() )
494 : {
495 0 : Reformat();
496 0 : SpinField::Modify();
497 : }
498 : }
499 0 : return SpinField::Notify( rNEvt );
500 : }
501 :
502 :
503 0 : void LongCurrencyField::Modify()
504 : {
505 0 : MarkToBeReformatted( sal_True );
506 0 : SpinField::Modify();
507 0 : }
508 :
509 :
510 0 : void LongCurrencyField::Up()
511 : {
512 0 : BigInt nValue = GetValue();
513 0 : nValue += mnSpinSize;
514 0 : if ( nValue > mnMax )
515 0 : nValue = mnMax;
516 :
517 0 : ImplNewLongCurrencyFieldValue( this, nValue );
518 0 : SpinField::Up();
519 0 : }
520 :
521 0 : void LongCurrencyField::Down()
522 : {
523 0 : BigInt nValue = GetValue();
524 0 : nValue -= mnSpinSize;
525 0 : if ( nValue < mnMin )
526 0 : nValue = mnMin;
527 :
528 0 : ImplNewLongCurrencyFieldValue( this, nValue );
529 0 : SpinField::Down();
530 0 : }
531 :
532 :
533 0 : void LongCurrencyField::First()
534 : {
535 0 : ImplNewLongCurrencyFieldValue( this, mnFirst );
536 0 : SpinField::First();
537 0 : }
538 :
539 :
540 0 : void LongCurrencyField::Last()
541 : {
542 0 : ImplNewLongCurrencyFieldValue( this, mnLast );
543 0 : SpinField::Last();
544 0 : }
545 :
546 0 : LongCurrencyBox::LongCurrencyBox( Window* pParent, WinBits nWinStyle ) :
547 0 : ComboBox( pParent, nWinStyle )
548 : {
549 0 : SetField( this );
550 0 : Reformat();
551 0 : }
552 :
553 0 : LongCurrencyBox::~LongCurrencyBox()
554 : {
555 0 : }
556 :
557 0 : long LongCurrencyBox::PreNotify( NotifyEvent& rNEvt )
558 : {
559 0 : if( rNEvt.GetType() == EVENT_KEYINPUT )
560 : {
561 0 : if ( ImplLongCurrencyProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), GetLocaleDataWrapper() ) )
562 0 : return 1;
563 : }
564 0 : return ComboBox::PreNotify( rNEvt );
565 : }
566 :
567 :
568 0 : long LongCurrencyBox::Notify( NotifyEvent& rNEvt )
569 : {
570 0 : if( rNEvt.GetType() == EVENT_GETFOCUS )
571 : {
572 0 : MarkToBeReformatted( sal_False );
573 : }
574 0 : else if( rNEvt.GetType() == EVENT_LOSEFOCUS )
575 : {
576 0 : if ( MustBeReformatted() )
577 : {
578 0 : Reformat();
579 0 : ComboBox::Modify();
580 : }
581 : }
582 0 : return ComboBox::Notify( rNEvt );
583 : }
584 :
585 0 : void LongCurrencyBox::Modify()
586 : {
587 0 : MarkToBeReformatted( sal_True );
588 0 : ComboBox::Modify();
589 0 : }
590 :
591 0 : void LongCurrencyBox::ReformatAll()
592 : {
593 0 : XubString aStr;
594 0 : SetUpdateMode( sal_False );
595 0 : sal_uInt16 nEntryCount = GetEntryCount();
596 0 : for ( sal_uInt16 i=0; i < nEntryCount; i++ )
597 : {
598 : ImplLongCurrencyReformat( GetEntry( i ), mnMin, mnMax,
599 0 : GetDecimalDigits(), GetLocaleDataWrapper(),
600 0 : aStr, *this );
601 0 : RemoveEntry( i );
602 0 : InsertEntry( aStr, i );
603 : }
604 0 : LongCurrencyFormatter::Reformat();
605 0 : SetUpdateMode( sal_True );
606 465 : }
607 :
608 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|