build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / control / longcurr.cxx
blob33ba8ad749164055c42ff30be208d49a033e46f0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <comphelper/string.hxx>
21 #include <tools/debug.hxx>
22 #include <tools/bigint.hxx>
24 #include <tools/rc.h>
26 #include <vcl/event.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/longcurr.hxx>
30 #include <svdata.hxx>
32 #include <unotools/localedatawrapper.hxx>
34 using namespace ::comphelper;
36 namespace
39 BigInt ImplPower10( sal_uInt16 n )
41 sal_uInt16 i;
42 BigInt nValue = 1;
44 for ( i=0; i < n; i++ )
45 nValue *= 10;
47 return nValue;
50 OUString ImplGetCurr( const LocaleDataWrapper& rLocaleDataWrapper, const BigInt &rNumber, sal_uInt16 nDigits, const OUString& rCurrSymbol, bool bShowThousandSep )
52 SAL_WARN_IF( nDigits >= 10, "vcl", "LongCurrency may only have 9 decimal places" );
54 if ( rNumber.IsZero() || (long)rNumber )
55 return rLocaleDataWrapper.getCurr( (long)rNumber, nDigits, rCurrSymbol, bShowThousandSep );
57 BigInt aTmp( ImplPower10( nDigits ) );
58 BigInt aInteger( rNumber );
59 aInteger.Abs();
60 aInteger /= aTmp;
61 BigInt aFraction( rNumber );
62 aFraction.Abs();
63 aFraction %= aTmp;
64 if ( !aInteger.IsZero() )
66 aFraction += aTmp;
67 aTmp = 1000000000;
69 if ( rNumber.IsNeg() )
70 aFraction *= -1;
72 OUStringBuffer aTemplate = rLocaleDataWrapper.getCurr( (long)aFraction, nDigits, rCurrSymbol, bShowThousandSep );
73 while( !aInteger.IsZero() )
75 aFraction = aInteger;
76 aFraction %= aTmp;
77 aInteger /= aTmp;
78 if( !aInteger.IsZero() )
79 aFraction += aTmp;
81 OUString aFractionStr = rLocaleDataWrapper.getNum( (long)aFraction, 0 );
83 sal_Int32 nSPos = aTemplate.indexOf( '1' );
84 if (nSPos == -1)
85 break;
86 if ( aFractionStr.getLength() == 1 )
87 aTemplate[ nSPos ] = aFractionStr[0];
88 else
90 aTemplate.remove( nSPos, 1 );
91 aTemplate.insert( nSPos, aFractionStr );
95 return aTemplate.makeStringAndClear();
98 bool ImplNumericProcessKeyInput( Edit*, const KeyEvent& rKEvt,
99 bool bStrictFormat, bool bThousandSep,
100 const LocaleDataWrapper& rLocaleDataWrapper )
102 if ( !bStrictFormat )
103 return false;
104 else
106 sal_Unicode cChar = rKEvt.GetCharCode();
107 sal_uInt16 nGroup = rKEvt.GetKeyCode().GetGroup();
109 if ( (nGroup == KEYGROUP_FKEYS) || (nGroup == KEYGROUP_CURSOR) ||
110 (nGroup == KEYGROUP_MISC) ||
111 ((cChar >= '0') && (cChar <= '9')) ||
112 (bThousandSep && string::equals(rLocaleDataWrapper.getNumThousandSep(), cChar)) ||
113 (string::equals(rLocaleDataWrapper.getNumDecimalSep(), cChar) ) ||
114 (cChar == '-') )
115 return false;
116 else
117 return true;
121 bool ImplNumericGetValue( const OUString& rStr, BigInt& rValue,
122 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper,
123 bool bCurrency )
125 OUString aStr = rStr;
126 OUStringBuffer aStr1;
127 OUStringBuffer aStr2;
128 sal_Int32 nDecPos;
129 bool bNegative = false;
131 // On empty string
132 if ( rStr.isEmpty() )
133 return false;
135 // Trim leading and trailing spaces
136 aStr = string::strip(aStr, ' ');
138 // Find decimal sign's position
139 nDecPos = aStr.indexOf( rLocaleDataWrapper.getNumDecimalSep() );
141 if ( nDecPos != -1 )
143 aStr1 = aStr.copy( 0, nDecPos );
144 aStr2.append(aStr.copy(nDecPos+1));
146 else
147 aStr1 = aStr;
149 // Negative?
150 if ( bCurrency )
152 if ( (aStr[ 0 ] == '(') && (aStr[ aStr.getLength()-1 ] == ')') )
153 bNegative = true;
154 if ( !bNegative )
156 for (sal_Int32 i=0; i < aStr.getLength(); i++ )
158 if ( (aStr[ i ] >= '0') && (aStr[ i ] <= '9') )
159 break;
160 else if ( aStr[ i ] == '-' )
162 bNegative = true;
163 break;
167 if ( !bNegative && bCurrency && !aStr.isEmpty() )
169 sal_uInt16 nFormat = rLocaleDataWrapper.getCurrNegativeFormat();
170 if ( (nFormat == 3) || (nFormat == 6) ||
171 (nFormat == 7) || (nFormat == 10) )
173 for (sal_Int32 i = aStr.getLength()-1; i > 0; i++ )
175 if ( (aStr[ i ] >= '0') && (aStr[ i ] <= '9') )
176 break;
177 else if ( aStr[ i ] == '-' )
179 bNegative = true;
180 break;
186 else
188 if ( aStr1[ 0 ] == '-' )
189 bNegative = true;
192 // delete unwanted characters
193 for (sal_Int32 i=0; i < aStr1.getLength(); )
195 if ( (aStr1[ i ] >= '0') && (aStr1[ i ] <= '9') )
196 i++;
197 else
198 aStr1.remove( i, 1 );
200 for (sal_Int32 i=0; i < aStr2.getLength(); )
202 if ((aStr2[i] >= '0') && (aStr2[i] <= '9'))
203 ++i;
204 else
205 aStr2.remove(i, 1);
208 if ( aStr1.isEmpty() && aStr2.isEmpty())
209 return false;
211 if ( aStr1.isEmpty() )
212 aStr1 = "0";
213 if ( bNegative )
214 aStr1.insert( 0, '-');
216 // Cut down decimal part and round while doing so
217 bool bRound = false;
218 if (aStr2.getLength() > nDecDigits)
220 if (aStr2[nDecDigits] >= '5')
221 bRound = true;
222 string::truncateToLength(aStr2, nDecDigits);
224 if (aStr2.getLength() < nDecDigits)
225 string::padToLength(aStr2, nDecDigits, '0');
227 aStr = aStr1.makeStringAndClear();
228 aStr += aStr2.makeStringAndClear();
230 // check range
231 BigInt nValue( aStr );
232 if ( bRound )
234 if ( !bNegative )
235 nValue+=1;
236 else
237 nValue-=1;
240 rValue = nValue;
242 return true;
245 bool ImplLongCurrencyProcessKeyInput( Edit* pEdit, const KeyEvent& rKEvt,
246 bool, bool bUseThousandSep, const LocaleDataWrapper& rLocaleDataWrapper )
248 // There's no StrictFormat that makes sense here, thus allow all chars
249 return ImplNumericProcessKeyInput( pEdit, rKEvt, false, bUseThousandSep, rLocaleDataWrapper );
252 } // namespace
254 inline bool ImplLongCurrencyGetValue( const OUString& rStr, BigInt& rValue,
255 sal_uInt16 nDecDigits, const LocaleDataWrapper& rLocaleDataWrapper )
257 return ImplNumericGetValue( rStr, rValue, nDecDigits, rLocaleDataWrapper, true );
260 bool ImplLongCurrencyReformat( const OUString& rStr, BigInt const & nMin, BigInt const & nMax,
261 sal_uInt16 nDecDigits,
262 const LocaleDataWrapper& rLocaleDataWrapper, OUString& rOutStr,
263 LongCurrencyFormatter& rFormatter )
265 BigInt nValue;
266 if ( !ImplNumericGetValue( rStr, nValue, nDecDigits, rLocaleDataWrapper, true ) )
267 return true;
268 else
270 BigInt nTempVal = nValue;
271 if ( nTempVal > nMax )
272 nTempVal = nMax;
273 else if ( nTempVal < nMin )
274 nTempVal = nMin;
276 rOutStr = ImplGetCurr( rLocaleDataWrapper, nTempVal, nDecDigits, rFormatter.GetCurrencySymbol(), rFormatter.IsUseThousandSep() );
277 return true;
281 void LongCurrencyFormatter::ImpInit()
283 mnFieldValue = 0;
284 mnLastValue = 0;
285 mnMin = 0;
286 mnMax = 0x7FFFFFFF;
287 mnMax *= 0x7FFFFFFF;
288 mnCorrectedValue = 0;
289 mnDecimalDigits = 0;
290 mbThousandSep = true;
291 SetDecimalDigits( 0 );
294 LongCurrencyFormatter::LongCurrencyFormatter()
296 ImpInit();
299 LongCurrencyFormatter::~LongCurrencyFormatter()
303 void LongCurrencyFormatter::SetCurrencySymbol( const OUString& rStr )
305 maCurrencySymbol= rStr;
306 ReformatAll();
309 OUString const & LongCurrencyFormatter::GetCurrencySymbol() const
311 return !maCurrencySymbol.isEmpty() ? maCurrencySymbol : GetLocaleDataWrapper().getCurrSymbol();
314 void LongCurrencyFormatter::SetValue(const BigInt& rNewValue)
316 SetUserValue(rNewValue);
317 mnFieldValue = mnLastValue;
318 SetEmptyFieldValueData( false );
321 void LongCurrencyFormatter::SetUserValue( BigInt nNewValue )
323 if ( nNewValue > mnMax )
324 nNewValue = mnMax;
325 else if ( nNewValue < mnMin )
326 nNewValue = mnMin;
327 mnLastValue = nNewValue;
329 if ( !GetField() )
330 return;
332 OUString aStr = ImplGetCurr( GetLocaleDataWrapper(), nNewValue, GetDecimalDigits(), GetCurrencySymbol(), IsUseThousandSep() );
333 if ( GetField()->HasFocus() )
335 Selection aSelection = GetField()->GetSelection();
336 GetField()->SetText( aStr );
337 GetField()->SetSelection( aSelection );
339 else
340 GetField()->SetText( aStr );
341 MarkToBeReformatted( false );
344 BigInt LongCurrencyFormatter::GetValue() const
346 if ( !GetField() )
347 return 0;
349 BigInt nTempValue;
350 if ( ImplLongCurrencyGetValue( GetField()->GetText(), nTempValue, GetDecimalDigits(), GetLocaleDataWrapper() ) )
352 if ( nTempValue > mnMax )
353 nTempValue = mnMax;
354 else if ( nTempValue < mnMin )
355 nTempValue = mnMin;
356 return nTempValue;
358 else
359 return mnLastValue;
362 void LongCurrencyFormatter::Reformat()
364 if ( !GetField() )
365 return;
367 if ( GetField()->GetText().isEmpty() && ImplGetEmptyFieldValue() )
368 return;
370 OUString aStr;
371 bool bOK = ImplLongCurrencyReformat( GetField()->GetText(), mnMin, mnMax,
372 GetDecimalDigits(), GetLocaleDataWrapper(), aStr, *this );
373 if ( !bOK )
374 return;
376 if ( !aStr.isEmpty() )
378 GetField()->SetText( aStr );
379 MarkToBeReformatted( false );
380 ImplLongCurrencyGetValue( aStr, mnLastValue, GetDecimalDigits(), GetLocaleDataWrapper() );
382 else
383 SetValue( mnLastValue );
386 void LongCurrencyFormatter::ReformatAll()
388 Reformat();
391 void LongCurrencyFormatter::SetMin(const BigInt& rNewMin)
393 mnMin = rNewMin;
394 ReformatAll();
397 void LongCurrencyFormatter::SetMax(const BigInt& rNewMax)
399 mnMax = rNewMax;
400 ReformatAll();
403 void LongCurrencyFormatter::SetUseThousandSep( bool b )
405 mbThousandSep = b;
406 ReformatAll();
409 void LongCurrencyFormatter::SetDecimalDigits( sal_uInt16 nDigits )
411 if ( nDigits > 9 )
412 nDigits = 9;
414 mnDecimalDigits = nDigits;
415 ReformatAll();
419 void ImplNewLongCurrencyFieldValue(LongCurrencyField* pField, const BigInt& rNewValue)
421 Selection aSelect = pField->GetSelection();
422 aSelect.Justify();
423 OUString aText = pField->GetText();
424 bool bLastSelected = aSelect.Max() == aText.getLength();
426 BigInt nOldLastValue = pField->mnLastValue;
427 pField->SetUserValue(rNewValue);
428 pField->mnLastValue = nOldLastValue;
430 if ( bLastSelected )
432 if ( !aSelect.Len() )
433 aSelect.Min() = SELECTION_MAX;
434 aSelect.Max() = SELECTION_MAX;
436 pField->SetSelection( aSelect );
437 pField->SetModifyFlag();
438 pField->Modify();
441 LongCurrencyField::LongCurrencyField( vcl::Window* pParent, WinBits nWinStyle ) :
442 SpinField( pParent, nWinStyle )
444 SetField( this );
445 mnSpinSize = 1;
446 mnFirst = mnMin;
447 mnLast = mnMax;
449 Reformat();
452 bool LongCurrencyField::PreNotify( NotifyEvent& rNEvt )
454 if( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
456 if ( ImplLongCurrencyProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), GetLocaleDataWrapper() ) )
457 return true;
459 return SpinField::PreNotify( rNEvt );
462 bool LongCurrencyField::EventNotify( NotifyEvent& rNEvt )
464 if( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
466 MarkToBeReformatted( false );
468 else if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
470 if ( MustBeReformatted() )
472 Reformat();
473 SpinField::Modify();
476 return SpinField::EventNotify( rNEvt );
479 void LongCurrencyField::Modify()
481 MarkToBeReformatted( true );
482 SpinField::Modify();
485 void LongCurrencyField::Up()
487 BigInt nValue = GetValue();
488 nValue += mnSpinSize;
489 if ( nValue > mnMax )
490 nValue = mnMax;
492 ImplNewLongCurrencyFieldValue( this, nValue );
493 SpinField::Up();
496 void LongCurrencyField::Down()
498 BigInt nValue = GetValue();
499 nValue -= mnSpinSize;
500 if ( nValue < mnMin )
501 nValue = mnMin;
503 ImplNewLongCurrencyFieldValue( this, nValue );
504 SpinField::Down();
507 void LongCurrencyField::First()
509 ImplNewLongCurrencyFieldValue( this, mnFirst );
510 SpinField::First();
513 void LongCurrencyField::Last()
515 ImplNewLongCurrencyFieldValue( this, mnLast );
516 SpinField::Last();
519 LongCurrencyBox::LongCurrencyBox( vcl::Window* pParent, WinBits nWinStyle ) :
520 ComboBox( pParent, nWinStyle )
522 SetField( this );
523 Reformat();
526 bool LongCurrencyBox::PreNotify( NotifyEvent& rNEvt )
528 if( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
530 if ( ImplLongCurrencyProcessKeyInput( GetField(), *rNEvt.GetKeyEvent(), IsStrictFormat(), IsUseThousandSep(), GetLocaleDataWrapper() ) )
531 return true;
533 return ComboBox::PreNotify( rNEvt );
536 bool LongCurrencyBox::EventNotify( NotifyEvent& rNEvt )
538 if( rNEvt.GetType() == MouseNotifyEvent::GETFOCUS )
540 MarkToBeReformatted( false );
542 else if( rNEvt.GetType() == MouseNotifyEvent::LOSEFOCUS )
544 if ( MustBeReformatted() )
546 Reformat();
547 ComboBox::Modify();
550 return ComboBox::EventNotify( rNEvt );
553 void LongCurrencyBox::Modify()
555 MarkToBeReformatted( true );
556 ComboBox::Modify();
559 void LongCurrencyBox::ReformatAll()
561 OUString aStr;
562 SetUpdateMode( false );
563 const sal_Int32 nEntryCount = GetEntryCount();
564 for ( sal_Int32 i=0; i < nEntryCount; ++i )
566 ImplLongCurrencyReformat( GetEntry( i ), mnMin, mnMax,
567 GetDecimalDigits(), GetLocaleDataWrapper(),
568 aStr, *this );
569 RemoveEntryAt(i);
570 InsertEntry( aStr, i );
572 LongCurrencyFormatter::Reformat();
573 SetUpdateMode( true );
576 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */