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