uifw/eikctl/src/aknmfnefloat.cpp
changeset 0 2f259fa3e83a
equal deleted inserted replaced
-1:000000000000 0:2f259fa3e83a
       
     1 /*
       
     2 * Copyright (c) 2005-2007 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Floating point field for CEikMfne
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 // INCLUDE FILES
       
    20 #include <e32math.h>
       
    21 #include <AknUtils.h>
       
    22 #include <numberconversion.h>
       
    23 
       
    24 #include "aknmfnefloat.h"
       
    25 
       
    26 namespace {
       
    27 
       
    28 const TInt KNumOtherSymbols( 2 ); // +/- and decimal point
       
    29 const TInt KTextBufferSize( 32 ); // should be enough
       
    30 const TInt KEventCodeStarSign( 42 );
       
    31 const TInt KEventCodeMinusSign( 45 );
       
    32 const TInt KKeyToMatchPoint( 35 ); // Shift/Hash
       
    33 
       
    34 #if defined(_DEBUG)
       
    35 void Panic(TInt aReason)
       
    36     {
       
    37     _LIT(KPanicCategory, "MFNE_FLT2");
       
    38     User::Panic(KPanicCategory, aReason);
       
    39     }
       
    40 #endif
       
    41     
       
    42 TReal Nan()
       
    43     {
       
    44     TRealX nan;
       
    45     nan.SetNaN();
       
    46     return nan;
       
    47     }
       
    48 
       
    49 TBool IsAnyDigit(TChar aCharacter)
       
    50 	{
       
    51 	return (aCharacter.GetNumericValue() != KErrNotFound);
       
    52 	}
       
    53 }
       
    54 
       
    55 
       
    56 CAknMfneFloat::CAknMfneFloat( TInt aMaxFractionalDigits ) : 
       
    57         iMaxFractionalDigits( aMaxFractionalDigits )
       
    58     {
       
    59     __ASSERT_DEBUG( aMaxFractionalDigits > 0, Panic( KErrArgument ) );
       
    60     }
       
    61 
       
    62 
       
    63 CAknMfneFloat::~CAknMfneFloat()
       
    64     {
       
    65     delete iText;
       
    66     }
       
    67 
       
    68 
       
    69 CAknMfneFloat* CAknMfneFloat::NewL( TInt aMaxFractionalDigits )
       
    70     {
       
    71     CAknMfneFloat* self = new ( ELeave )
       
    72         CAknMfneFloat( aMaxFractionalDigits );
       
    73 
       
    74     CleanupStack::PushL( self );
       
    75     self->ConstructL();
       
    76     CleanupStack::Pop();
       
    77 
       
    78     return self;
       
    79     }
       
    80 
       
    81 
       
    82 void CAknMfneFloat::ConstructL()
       
    83     {
       
    84     iText = HBufC::NewL( KTextBufferSize );
       
    85     RefreshDigitType();
       
    86     UpdateAllowedInput();
       
    87     }
       
    88     
       
    89 // ---------------------------------------------------------
       
    90 // Makes sure that the value in the editor is normalized,
       
    91 // i.e. within limits and formatted correctly.
       
    92 // ---------------------------------------------------------
       
    93 TBool CAknMfneFloat::SetTextFromValueAndNormalize( TReal aValue )
       
    94     {
       
    95     TBool reportError( EFalse );
       
    96     TPtr ptr( iText->Des() );
       
    97     
       
    98     if ( Math::IsNaN( aValue ) && !( iFlags & EAllowNaN ) )
       
    99         {
       
   100         aValue = iMaximumValue;
       
   101         reportError = ETrue;
       
   102         }
       
   103         
       
   104     if ( !Math::IsNaN( aValue ) )
       
   105         {
       
   106         // 1) Check the minimum and maximum limits
       
   107         if ( aValue < iMinimumValue )
       
   108             {
       
   109             aValue = iMinimumValue;
       
   110             reportError = ETrue;
       
   111             }
       
   112         else if ( aValue > iMaximumValue )
       
   113             {
       
   114             aValue = iMaximumValue;
       
   115             reportError = ETrue;
       
   116             }
       
   117             
       
   118         // 2) Round according to iMaxFractionalDigits
       
   119         TReal roundedValue;
       
   120         Math::Round( roundedValue, aValue, iMaxFractionalDigits );
       
   121 
       
   122         // 3) Enforce the limits in case change in fractional digits
       
   123         //    causes erroneous rounding. An example case is where
       
   124         //    max limit = 1.56, value = 1.55, max frac digits becomes = 1
       
   125         //    roundedValue = 1.6 ( > max limit, no good )
       
   126         TReal corrector;
       
   127         Math::Pow10( corrector, -iMaxFractionalDigits );
       
   128         
       
   129         if ( roundedValue > iMaximumValue )
       
   130             {
       
   131             roundedValue -= corrector;
       
   132             }
       
   133         else if ( roundedValue < iMinimumValue )
       
   134             {
       
   135             roundedValue += corrector;
       
   136             }
       
   137 
       
   138         // 4) Convert real value to a string, TRealFormat is mandatory
       
   139         TRealFormat format;
       
   140         format.iType   = KRealFormatFixed;
       
   141         format.iTriLen = 0; // no triad separations
       
   142         format.iWidth  = MaxNumOfChars();
       
   143         format.iPlaces = ( roundedValue == 0 ) ? 0 : iMaxFractionalDigits;
       
   144 
       
   145         ptr.Num( roundedValue, format );
       
   146         
       
   147         // 5) Cut trailing zeroes and separator from decimal part
       
   148         TChar decimalSeparator( DecimalSeparator() );
       
   149         TInt separatorPos = ptr.Locate( decimalSeparator );
       
   150         
       
   151         if ( separatorPos != KErrNotFound )
       
   152             {
       
   153             TInt len = ptr.Length();
       
   154             
       
   155             while ( len > separatorPos && ( ptr[len-1] == '0' || ptr[len-1] == decimalSeparator ) )
       
   156                 {
       
   157                 len--;
       
   158                 }
       
   159             
       
   160             ptr.SetLength( len );
       
   161             }
       
   162 
       
   163         // 6) Finally convert to iDigitType
       
   164         AknTextUtils::ConvertDigitsTo( ptr, iDigitType );
       
   165         }
       
   166     else
       
   167         {
       
   168         iText->Des().Zero();
       
   169         }
       
   170 
       
   171     iIsBeingEditedWithCursor = EFalse;
       
   172     
       
   173     UpdateAllowedInput();
       
   174     
       
   175     return reportError;         
       
   176     }
       
   177 
       
   178 TChar CAknMfneFloat::DecimalSeparator() const
       
   179     {
       
   180     TLocale locale;
       
   181     return locale.DecimalSeparator();
       
   182     }
       
   183 
       
   184 void CAknMfneFloat::UpdateAllowedInput()
       
   185     {
       
   186     TInt textLength = iText->Length();
       
   187     
       
   188     // Various highly annoyingly non-generic cases:
       
   189     // 1) Highlighted or "": Allow conditional digit and conditional minus sign
       
   190     // 2) Starts with only "0" or "-0": Allow only conditional decimal separator
       
   191     // 3) Starts with only "-": Allow only digit
       
   192     // 4) Allow digits depending on the length of integer and fractional part
       
   193     
       
   194     TInt decimalSeparatorFlag = ( iMaxFractionalDigits > 0 ? EDecimalSeparator : 0 );
       
   195     TInt minusSignFlag = ( iMinimumValue < 0 ? EMinusSign : 0 );
       
   196 
       
   197     // 1) .. and return without doing anything else
       
   198     if ( !iIsBeingEditedWithCursor || textLength == 0 )
       
   199         {
       
   200         iAllowedInputFlags = minusSignFlag | ( iMaximumValue >= 0 ? EDigit : 0 );
       
   201         return;
       
   202         }
       
   203         
       
   204     // 2) .. also return without doing anything else
       
   205     TBuf<2> minusAndZero;
       
   206     minusAndZero.Append( iMinusSign );
       
   207     minusAndZero.Append( iZero );
       
   208     
       
   209     if ( textLength == 1 && (*iText)[0] == iZero ||
       
   210          iText->Des() == minusAndZero )
       
   211         {
       
   212         iAllowedInputFlags = decimalSeparatorFlag;
       
   213         return;
       
   214         }
       
   215         
       
   216     // 3) .. also return without doing anything else
       
   217     if ( textLength == 1 && (*iText)[0] == iMinusSign )
       
   218         {
       
   219         iAllowedInputFlags = EDigit;
       
   220         return;
       
   221         }
       
   222 
       
   223     TChar separator( DecimalSeparator() );
       
   224     TChar lastChar( 0 );
       
   225     TInt separatorPos( KErrNotFound );
       
   226     TInt minusPos( KErrNotFound );
       
   227     TInt integerDigits( 0 );
       
   228     TInt fractionalDigits( 0 );
       
   229     
       
   230     // For 4), more info needed, find everything in one loop
       
   231     for ( TInt i = 0; i < textLength; ++i )
       
   232         {
       
   233         lastChar = (*iText)[i];
       
   234         
       
   235         if ( IsAnyDigit( lastChar ) )
       
   236             {
       
   237             if ( separatorPos >= 0 )
       
   238                 {
       
   239                 fractionalDigits++;    
       
   240                 }
       
   241             else
       
   242                 {
       
   243                 integerDigits++;
       
   244                 }
       
   245             }
       
   246         else if ( lastChar == iMinusSign )
       
   247             {
       
   248             minusPos = i;
       
   249             }
       
   250         else if ( lastChar == separator )
       
   251             {
       
   252             separatorPos = i;
       
   253             }
       
   254         }
       
   255     
       
   256     iAllowedInputFlags = 0;
       
   257     
       
   258     // 4)
       
   259     if ( separatorPos < 0 )
       
   260         {
       
   261         iAllowedInputFlags = decimalSeparatorFlag;
       
   262         
       
   263         if ( ( minusPos >= 0 && integerDigits < iMaxNegativeIntegerDigits ) ||
       
   264              ( minusPos < 0 && integerDigits < iMaxPositiveIntegerDigits ) )
       
   265             {
       
   266             iAllowedInputFlags |= EDigit;
       
   267             }        
       
   268         }
       
   269     else if ( fractionalDigits < iMaxFractionalDigits )
       
   270         {
       
   271         iAllowedInputFlags = EDigit;
       
   272         }
       
   273     }
       
   274 
       
   275 
       
   276 void CAknMfneFloat::ClearFieldIfNecessary( TBool& aDataAltered )
       
   277     {
       
   278     if ( !iIsBeingEditedWithCursor )
       
   279         {
       
   280         iIsBeingEditedWithCursor = ETrue;
       
   281         TPtr ptr( iText->Des() );
       
   282         ptr.Zero();
       
   283         UpdateAllowedInput();
       
   284         aDataAltered = ETrue;
       
   285         }
       
   286     }
       
   287     
       
   288     
       
   289 void CAknMfneFloat::TryAppend( TChar aChar, TUint aAllowedFlags, TBool& aDataAltered )
       
   290     {
       
   291     if ( iAllowedInputFlags & aAllowedFlags )
       
   292         {
       
   293         ClearFieldIfNecessary( aDataAltered );
       
   294         iText->Des().Append( aChar );
       
   295         aDataAltered = ETrue;
       
   296         }
       
   297     }
       
   298     
       
   299     
       
   300 void CAknMfneFloat::TryDelete( TInt aDigits, TBool& aDataAltered )
       
   301     {
       
   302     ClearFieldIfNecessary( aDataAltered );
       
   303     
       
   304     TPtr ptr( iText->Des() );
       
   305     TInt delLength( ptr.Length() - aDigits );
       
   306     
       
   307     if ( delLength >= 0 )
       
   308         {
       
   309         ptr.SetLength( delLength );
       
   310         aDataAltered = ETrue;
       
   311         }
       
   312     }
       
   313 
       
   314 TChar CAknMfneFloat::NormalizeDigit( TChar aChar ) const
       
   315 	{
       
   316 	TBuf<1> buf;
       
   317 	buf.Append( aChar );
       
   318 	NumberConversion::ConvertDigits( buf, iDigitType );
       
   319 	return buf[0];
       
   320 	}    
       
   321 
       
   322 TBool CAknMfneFloat::SetLimits(
       
   323     TReal aMinimumValue, 
       
   324     TReal aMaximumValue)
       
   325     {
       
   326     __ASSERT_DEBUG(
       
   327         Math::IsNaN(aMinimumValue) ||
       
   328         Math::IsNaN(aMaximumValue) ||
       
   329         (aMinimumValue <= aMaximumValue), Panic(KErrArgument));
       
   330         
       
   331     // In release, silently fix client's error?
       
   332     if ( aMinimumValue > aMaximumValue )
       
   333         {
       
   334         iMinimumValue = aMaximumValue;
       
   335         iMaximumValue = aMinimumValue;
       
   336         }
       
   337     else
       
   338         {
       
   339         iMinimumValue = aMinimumValue;
       
   340         iMaximumValue = aMaximumValue;
       
   341         }
       
   342     
       
   343     TBuf<KTextBufferSize> numDigits;
       
   344     numDigits.Num( Abs( aMinimumValue ) ); // implicit truncation to TInt
       
   345     iMaxNegativeIntegerDigits = numDigits.Length();
       
   346     numDigits.Num( Abs( aMaximumValue ) );
       
   347     iMaxPositiveIntegerDigits = numDigits.Length();
       
   348     
       
   349     return NormalizeValue();
       
   350     }
       
   351 
       
   352 
       
   353 void CAknMfneFloat::GetLimits(
       
   354     TReal& aMinimumValue, 
       
   355     TReal& aMaximumValue) const
       
   356     {
       
   357     aMinimumValue = iMinimumValue;
       
   358     aMaximumValue = iMaximumValue;
       
   359     }
       
   360 
       
   361 
       
   362 TBool CAknMfneFloat::SetValue( TReal aValue )
       
   363     {
       
   364     return SetTextFromValueAndNormalize( aValue );
       
   365     }
       
   366 
       
   367 
       
   368 TReal CAknMfneFloat::Value() const
       
   369     {
       
   370     return ValueFromText( *iText );
       
   371     }
       
   372 
       
   373 
       
   374 TBool CAknMfneFloat::NormalizeValue()
       
   375     {
       
   376     return SetTextFromValueAndNormalize( Value() );
       
   377     }
       
   378     
       
   379 
       
   380 TInt CAknMfneFloat::MaximumWidthInPixels(
       
   381     const CFont& aFont, 
       
   382     TBool /*aShrinkToMinimumSize*/)
       
   383     {
       
   384     TLocale locale;
       
   385     TInt digitWidth = 
       
   386         TFindWidthOfWidestDigitType(locale.DigitType()).MaximumWidthInPixels(aFont);
       
   387         
       
   388     return MaxNumOfChars() * digitWidth;
       
   389     }
       
   390 
       
   391 
       
   392 TCoeInputCapabilities CAknMfneFloat::InputCapabilities() const
       
   393     {
       
   394     return TCoeInputCapabilities(TCoeInputCapabilities::EWesternNumericReal);
       
   395     }
       
   396 
       
   397 
       
   398 TBool CAknMfneFloat::IsEditable() const
       
   399     {
       
   400     return ETrue;
       
   401     }
       
   402 
       
   403 
       
   404 CEikMfneField::THighlightType CAknMfneFloat::HighlightType() const
       
   405     {
       
   406     return iIsBeingEditedWithCursor ? ECursor : EInverseVideo;
       
   407     }
       
   408 
       
   409 
       
   410 void CAknMfneFloat::HandleDeHighlight(
       
   411     const CFont& /*aFont*/, 
       
   412     CEikonEnv& /*aEikonEnv*/, 
       
   413     TBool& /*aDataAltered*/, 
       
   414     TBool& aError)
       
   415     {
       
   416     if ( NormalizeValue() )
       
   417         {
       
   418         CEikMfne::InvalidFieldAlert();
       
   419         aError = ETrue;
       
   420         }
       
   421     }
       
   422 
       
   423 
       
   424 void CAknMfneFloat::HandleKey(
       
   425     const CFont& /*aFont*/, 
       
   426     const TKeyEvent& aKeyEvent, 
       
   427     TBool /*aInterpretLeftAndRightAsEarEvents*/, 
       
   428     TBool& aDataAltered, 
       
   429     TInt& aHighlightIncrement)
       
   430     {
       
   431     TChar ch( aKeyEvent.iCode );
       
   432     TChar decimalSeparator( DecimalSeparator() );
       
   433     
       
   434     switch ( ch )
       
   435         {
       
   436         case EKeyLeftArrow:
       
   437         case EKeyRightArrow:
       
   438             HandleLeftOrRightArrow( ch, aDataAltered, aHighlightIncrement );
       
   439             break;
       
   440 
       
   441         case EKeyBackspace:
       
   442             TryDelete( 1, aDataAltered );
       
   443             break;
       
   444 
       
   445         case KEventCodeStarSign:
       
   446         case KEventCodeMinusSign:
       
   447             TryAppend( iMinusSign, EMinusSign, aDataAltered );
       
   448             break;
       
   449             
       
   450         default:
       
   451             if ( IsAnyDigit( ch ) )
       
   452                 {
       
   453                 TryAppend( NormalizeDigit( ch ), EDigit, aDataAltered );
       
   454                 }
       
   455             else if ( ch == KKeyToMatchPoint || ch == decimalSeparator )
       
   456                 {
       
   457                 TryAppend( decimalSeparator, EDecimalSeparator, aDataAltered );
       
   458                 }
       
   459             break;
       
   460         }
       
   461         
       
   462     UpdateAllowedInput();
       
   463     
       
   464     if ( !iAllowedInputFlags )
       
   465         {
       
   466         aHighlightIncrement = 1;
       
   467         }
       
   468     }
       
   469 
       
   470 
       
   471 const TDesC& CAknMfneFloat::Text() const
       
   472     {
       
   473     return *iText;
       
   474     }
       
   475 
       
   476 
       
   477 TInt CAknMfneFloat::MaxNumOfChars() const
       
   478     {
       
   479     return Max( iMaxNegativeIntegerDigits, iMaxPositiveIntegerDigits )
       
   480         + iMaxFractionalDigits + KNumOtherSymbols;
       
   481     }
       
   482 
       
   483 
       
   484 TReal CAknMfneFloat::ValueFromText( const TDesC& aText ) const
       
   485     {
       
   486     if ( aText.Length() == 0 )
       
   487         {
       
   488         return Nan();
       
   489         }
       
   490         
       
   491     // Sadly, TLex doesn't seem to handle arabic digits
       
   492     TReal real( 0 );
       
   493     
       
   494     HBufC* buf = HBufC::New( aText.Length() );
       
   495     
       
   496     if ( buf )
       
   497         {
       
   498         TPtr ptr( buf->Des() );
       
   499         ptr.Copy( aText );
       
   500         AknTextUtils::ConvertDigitsTo( ptr, EDigitTypeWestern );
       
   501         TLex lex( ptr );
       
   502         lex.Val( real );
       
   503         delete buf;
       
   504         }
       
   505         
       
   506     return real;
       
   507     }
       
   508 
       
   509         
       
   510 void CAknMfneFloat::SetMaxFractionalDigits( TInt aMaxFractionalDigits )
       
   511     {
       
   512     iMaxFractionalDigits = aMaxFractionalDigits;
       
   513     
       
   514     NormalizeValue();
       
   515     
       
   516     iIsBeingEditedWithCursor = EFalse;
       
   517     }
       
   518     
       
   519     
       
   520 TInt CAknMfneFloat::MaxFractionalDigits() const
       
   521     {
       
   522     return iMaxFractionalDigits;
       
   523     }
       
   524     
       
   525     
       
   526 void CAknMfneFloat::RefreshDigitType()
       
   527     {
       
   528     SetDigitType( AknTextUtils::NumericEditorDigitType() );
       
   529     }
       
   530 
       
   531 
       
   532 void CAknMfneFloat::SetDigitType( TDigitType aDigitType )
       
   533     {
       
   534     iDigitType = aDigitType;
       
   535     iZero      = iDigitType;
       
   536     iMinusSign = TChar( '-' );
       
   537     
       
   538     NormalizeValue();
       
   539     }
       
   540 
       
   541 
       
   542 TDigitType CAknMfneFloat::DigitType() const
       
   543     {
       
   544     return iDigitType;
       
   545     }
       
   546     
       
   547         
       
   548 void CAknMfneFloat::SetFlags( TUint aFlags )
       
   549     {
       
   550     iFlags = aFlags;
       
   551     NormalizeValue();
       
   552     }
       
   553     
       
   554 TUint CAknMfneFloat::Flags() const
       
   555     {
       
   556     return iFlags;
       
   557     }