uifw/eikctl/src/aknmfnefloat.cpp
changeset 0 2f259fa3e83a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uifw/eikctl/src/aknmfnefloat.cpp	Tue Feb 02 01:00:49 2010 +0200
@@ -0,0 +1,557 @@
+/*
+* Copyright (c) 2005-2007 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Floating point field for CEikMfne
+*
+*/
+
+
+// INCLUDE FILES
+#include <e32math.h>
+#include <AknUtils.h>
+#include <numberconversion.h>
+
+#include "aknmfnefloat.h"
+
+namespace {
+
+const TInt KNumOtherSymbols( 2 ); // +/- and decimal point
+const TInt KTextBufferSize( 32 ); // should be enough
+const TInt KEventCodeStarSign( 42 );
+const TInt KEventCodeMinusSign( 45 );
+const TInt KKeyToMatchPoint( 35 ); // Shift/Hash
+
+#if defined(_DEBUG)
+void Panic(TInt aReason)
+    {
+    _LIT(KPanicCategory, "MFNE_FLT2");
+    User::Panic(KPanicCategory, aReason);
+    }
+#endif
+    
+TReal Nan()
+    {
+    TRealX nan;
+    nan.SetNaN();
+    return nan;
+    }
+
+TBool IsAnyDigit(TChar aCharacter)
+	{
+	return (aCharacter.GetNumericValue() != KErrNotFound);
+	}
+}
+
+
+CAknMfneFloat::CAknMfneFloat( TInt aMaxFractionalDigits ) : 
+        iMaxFractionalDigits( aMaxFractionalDigits )
+    {
+    __ASSERT_DEBUG( aMaxFractionalDigits > 0, Panic( KErrArgument ) );
+    }
+
+
+CAknMfneFloat::~CAknMfneFloat()
+    {
+    delete iText;
+    }
+
+
+CAknMfneFloat* CAknMfneFloat::NewL( TInt aMaxFractionalDigits )
+    {
+    CAknMfneFloat* self = new ( ELeave )
+        CAknMfneFloat( aMaxFractionalDigits );
+
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop();
+
+    return self;
+    }
+
+
+void CAknMfneFloat::ConstructL()
+    {
+    iText = HBufC::NewL( KTextBufferSize );
+    RefreshDigitType();
+    UpdateAllowedInput();
+    }
+    
+// ---------------------------------------------------------
+// Makes sure that the value in the editor is normalized,
+// i.e. within limits and formatted correctly.
+// ---------------------------------------------------------
+TBool CAknMfneFloat::SetTextFromValueAndNormalize( TReal aValue )
+    {
+    TBool reportError( EFalse );
+    TPtr ptr( iText->Des() );
+    
+    if ( Math::IsNaN( aValue ) && !( iFlags & EAllowNaN ) )
+        {
+        aValue = iMaximumValue;
+        reportError = ETrue;
+        }
+        
+    if ( !Math::IsNaN( aValue ) )
+        {
+        // 1) Check the minimum and maximum limits
+        if ( aValue < iMinimumValue )
+            {
+            aValue = iMinimumValue;
+            reportError = ETrue;
+            }
+        else if ( aValue > iMaximumValue )
+            {
+            aValue = iMaximumValue;
+            reportError = ETrue;
+            }
+            
+        // 2) Round according to iMaxFractionalDigits
+        TReal roundedValue;
+        Math::Round( roundedValue, aValue, iMaxFractionalDigits );
+
+        // 3) Enforce the limits in case change in fractional digits
+        //    causes erroneous rounding. An example case is where
+        //    max limit = 1.56, value = 1.55, max frac digits becomes = 1
+        //    roundedValue = 1.6 ( > max limit, no good )
+        TReal corrector;
+        Math::Pow10( corrector, -iMaxFractionalDigits );
+        
+        if ( roundedValue > iMaximumValue )
+            {
+            roundedValue -= corrector;
+            }
+        else if ( roundedValue < iMinimumValue )
+            {
+            roundedValue += corrector;
+            }
+
+        // 4) Convert real value to a string, TRealFormat is mandatory
+        TRealFormat format;
+        format.iType   = KRealFormatFixed;
+        format.iTriLen = 0; // no triad separations
+        format.iWidth  = MaxNumOfChars();
+        format.iPlaces = ( roundedValue == 0 ) ? 0 : iMaxFractionalDigits;
+
+        ptr.Num( roundedValue, format );
+        
+        // 5) Cut trailing zeroes and separator from decimal part
+        TChar decimalSeparator( DecimalSeparator() );
+        TInt separatorPos = ptr.Locate( decimalSeparator );
+        
+        if ( separatorPos != KErrNotFound )
+            {
+            TInt len = ptr.Length();
+            
+            while ( len > separatorPos && ( ptr[len-1] == '0' || ptr[len-1] == decimalSeparator ) )
+                {
+                len--;
+                }
+            
+            ptr.SetLength( len );
+            }
+
+        // 6) Finally convert to iDigitType
+        AknTextUtils::ConvertDigitsTo( ptr, iDigitType );
+        }
+    else
+        {
+        iText->Des().Zero();
+        }
+
+    iIsBeingEditedWithCursor = EFalse;
+    
+    UpdateAllowedInput();
+    
+    return reportError;         
+    }
+
+TChar CAknMfneFloat::DecimalSeparator() const
+    {
+    TLocale locale;
+    return locale.DecimalSeparator();
+    }
+
+void CAknMfneFloat::UpdateAllowedInput()
+    {
+    TInt textLength = iText->Length();
+    
+    // Various highly annoyingly non-generic cases:
+    // 1) Highlighted or "": Allow conditional digit and conditional minus sign
+    // 2) Starts with only "0" or "-0": Allow only conditional decimal separator
+    // 3) Starts with only "-": Allow only digit
+    // 4) Allow digits depending on the length of integer and fractional part
+    
+    TInt decimalSeparatorFlag = ( iMaxFractionalDigits > 0 ? EDecimalSeparator : 0 );
+    TInt minusSignFlag = ( iMinimumValue < 0 ? EMinusSign : 0 );
+
+    // 1) .. and return without doing anything else
+    if ( !iIsBeingEditedWithCursor || textLength == 0 )
+        {
+        iAllowedInputFlags = minusSignFlag | ( iMaximumValue >= 0 ? EDigit : 0 );
+        return;
+        }
+        
+    // 2) .. also return without doing anything else
+    TBuf<2> minusAndZero;
+    minusAndZero.Append( iMinusSign );
+    minusAndZero.Append( iZero );
+    
+    if ( textLength == 1 && (*iText)[0] == iZero ||
+         iText->Des() == minusAndZero )
+        {
+        iAllowedInputFlags = decimalSeparatorFlag;
+        return;
+        }
+        
+    // 3) .. also return without doing anything else
+    if ( textLength == 1 && (*iText)[0] == iMinusSign )
+        {
+        iAllowedInputFlags = EDigit;
+        return;
+        }
+
+    TChar separator( DecimalSeparator() );
+    TChar lastChar( 0 );
+    TInt separatorPos( KErrNotFound );
+    TInt minusPos( KErrNotFound );
+    TInt integerDigits( 0 );
+    TInt fractionalDigits( 0 );
+    
+    // For 4), more info needed, find everything in one loop
+    for ( TInt i = 0; i < textLength; ++i )
+        {
+        lastChar = (*iText)[i];
+        
+        if ( IsAnyDigit( lastChar ) )
+            {
+            if ( separatorPos >= 0 )
+                {
+                fractionalDigits++;    
+                }
+            else
+                {
+                integerDigits++;
+                }
+            }
+        else if ( lastChar == iMinusSign )
+            {
+            minusPos = i;
+            }
+        else if ( lastChar == separator )
+            {
+            separatorPos = i;
+            }
+        }
+    
+    iAllowedInputFlags = 0;
+    
+    // 4)
+    if ( separatorPos < 0 )
+        {
+        iAllowedInputFlags = decimalSeparatorFlag;
+        
+        if ( ( minusPos >= 0 && integerDigits < iMaxNegativeIntegerDigits ) ||
+             ( minusPos < 0 && integerDigits < iMaxPositiveIntegerDigits ) )
+            {
+            iAllowedInputFlags |= EDigit;
+            }        
+        }
+    else if ( fractionalDigits < iMaxFractionalDigits )
+        {
+        iAllowedInputFlags = EDigit;
+        }
+    }
+
+
+void CAknMfneFloat::ClearFieldIfNecessary( TBool& aDataAltered )
+    {
+    if ( !iIsBeingEditedWithCursor )
+        {
+        iIsBeingEditedWithCursor = ETrue;
+        TPtr ptr( iText->Des() );
+        ptr.Zero();
+        UpdateAllowedInput();
+        aDataAltered = ETrue;
+        }
+    }
+    
+    
+void CAknMfneFloat::TryAppend( TChar aChar, TUint aAllowedFlags, TBool& aDataAltered )
+    {
+    if ( iAllowedInputFlags & aAllowedFlags )
+        {
+        ClearFieldIfNecessary( aDataAltered );
+        iText->Des().Append( aChar );
+        aDataAltered = ETrue;
+        }
+    }
+    
+    
+void CAknMfneFloat::TryDelete( TInt aDigits, TBool& aDataAltered )
+    {
+    ClearFieldIfNecessary( aDataAltered );
+    
+    TPtr ptr( iText->Des() );
+    TInt delLength( ptr.Length() - aDigits );
+    
+    if ( delLength >= 0 )
+        {
+        ptr.SetLength( delLength );
+        aDataAltered = ETrue;
+        }
+    }
+
+TChar CAknMfneFloat::NormalizeDigit( TChar aChar ) const
+	{
+	TBuf<1> buf;
+	buf.Append( aChar );
+	NumberConversion::ConvertDigits( buf, iDigitType );
+	return buf[0];
+	}    
+
+TBool CAknMfneFloat::SetLimits(
+    TReal aMinimumValue, 
+    TReal aMaximumValue)
+    {
+    __ASSERT_DEBUG(
+        Math::IsNaN(aMinimumValue) ||
+        Math::IsNaN(aMaximumValue) ||
+        (aMinimumValue <= aMaximumValue), Panic(KErrArgument));
+        
+    // In release, silently fix client's error?
+    if ( aMinimumValue > aMaximumValue )
+        {
+        iMinimumValue = aMaximumValue;
+        iMaximumValue = aMinimumValue;
+        }
+    else
+        {
+        iMinimumValue = aMinimumValue;
+        iMaximumValue = aMaximumValue;
+        }
+    
+    TBuf<KTextBufferSize> numDigits;
+    numDigits.Num( Abs( aMinimumValue ) ); // implicit truncation to TInt
+    iMaxNegativeIntegerDigits = numDigits.Length();
+    numDigits.Num( Abs( aMaximumValue ) );
+    iMaxPositiveIntegerDigits = numDigits.Length();
+    
+    return NormalizeValue();
+    }
+
+
+void CAknMfneFloat::GetLimits(
+    TReal& aMinimumValue, 
+    TReal& aMaximumValue) const
+    {
+    aMinimumValue = iMinimumValue;
+    aMaximumValue = iMaximumValue;
+    }
+
+
+TBool CAknMfneFloat::SetValue( TReal aValue )
+    {
+    return SetTextFromValueAndNormalize( aValue );
+    }
+
+
+TReal CAknMfneFloat::Value() const
+    {
+    return ValueFromText( *iText );
+    }
+
+
+TBool CAknMfneFloat::NormalizeValue()
+    {
+    return SetTextFromValueAndNormalize( Value() );
+    }
+    
+
+TInt CAknMfneFloat::MaximumWidthInPixels(
+    const CFont& aFont, 
+    TBool /*aShrinkToMinimumSize*/)
+    {
+    TLocale locale;
+    TInt digitWidth = 
+        TFindWidthOfWidestDigitType(locale.DigitType()).MaximumWidthInPixels(aFont);
+        
+    return MaxNumOfChars() * digitWidth;
+    }
+
+
+TCoeInputCapabilities CAknMfneFloat::InputCapabilities() const
+    {
+    return TCoeInputCapabilities(TCoeInputCapabilities::EWesternNumericReal);
+    }
+
+
+TBool CAknMfneFloat::IsEditable() const
+    {
+    return ETrue;
+    }
+
+
+CEikMfneField::THighlightType CAknMfneFloat::HighlightType() const
+    {
+    return iIsBeingEditedWithCursor ? ECursor : EInverseVideo;
+    }
+
+
+void CAknMfneFloat::HandleDeHighlight(
+    const CFont& /*aFont*/, 
+    CEikonEnv& /*aEikonEnv*/, 
+    TBool& /*aDataAltered*/, 
+    TBool& aError)
+    {
+    if ( NormalizeValue() )
+        {
+        CEikMfne::InvalidFieldAlert();
+        aError = ETrue;
+        }
+    }
+
+
+void CAknMfneFloat::HandleKey(
+    const CFont& /*aFont*/, 
+    const TKeyEvent& aKeyEvent, 
+    TBool /*aInterpretLeftAndRightAsEarEvents*/, 
+    TBool& aDataAltered, 
+    TInt& aHighlightIncrement)
+    {
+    TChar ch( aKeyEvent.iCode );
+    TChar decimalSeparator( DecimalSeparator() );
+    
+    switch ( ch )
+        {
+        case EKeyLeftArrow:
+        case EKeyRightArrow:
+            HandleLeftOrRightArrow( ch, aDataAltered, aHighlightIncrement );
+            break;
+
+        case EKeyBackspace:
+            TryDelete( 1, aDataAltered );
+            break;
+
+        case KEventCodeStarSign:
+        case KEventCodeMinusSign:
+            TryAppend( iMinusSign, EMinusSign, aDataAltered );
+            break;
+            
+        default:
+            if ( IsAnyDigit( ch ) )
+                {
+                TryAppend( NormalizeDigit( ch ), EDigit, aDataAltered );
+                }
+            else if ( ch == KKeyToMatchPoint || ch == decimalSeparator )
+                {
+                TryAppend( decimalSeparator, EDecimalSeparator, aDataAltered );
+                }
+            break;
+        }
+        
+    UpdateAllowedInput();
+    
+    if ( !iAllowedInputFlags )
+        {
+        aHighlightIncrement = 1;
+        }
+    }
+
+
+const TDesC& CAknMfneFloat::Text() const
+    {
+    return *iText;
+    }
+
+
+TInt CAknMfneFloat::MaxNumOfChars() const
+    {
+    return Max( iMaxNegativeIntegerDigits, iMaxPositiveIntegerDigits )
+        + iMaxFractionalDigits + KNumOtherSymbols;
+    }
+
+
+TReal CAknMfneFloat::ValueFromText( const TDesC& aText ) const
+    {
+    if ( aText.Length() == 0 )
+        {
+        return Nan();
+        }
+        
+    // Sadly, TLex doesn't seem to handle arabic digits
+    TReal real( 0 );
+    
+    HBufC* buf = HBufC::New( aText.Length() );
+    
+    if ( buf )
+        {
+        TPtr ptr( buf->Des() );
+        ptr.Copy( aText );
+        AknTextUtils::ConvertDigitsTo( ptr, EDigitTypeWestern );
+        TLex lex( ptr );
+        lex.Val( real );
+        delete buf;
+        }
+        
+    return real;
+    }
+
+        
+void CAknMfneFloat::SetMaxFractionalDigits( TInt aMaxFractionalDigits )
+    {
+    iMaxFractionalDigits = aMaxFractionalDigits;
+    
+    NormalizeValue();
+    
+    iIsBeingEditedWithCursor = EFalse;
+    }
+    
+    
+TInt CAknMfneFloat::MaxFractionalDigits() const
+    {
+    return iMaxFractionalDigits;
+    }
+    
+    
+void CAknMfneFloat::RefreshDigitType()
+    {
+    SetDigitType( AknTextUtils::NumericEditorDigitType() );
+    }
+
+
+void CAknMfneFloat::SetDigitType( TDigitType aDigitType )
+    {
+    iDigitType = aDigitType;
+    iZero      = iDigitType;
+    iMinusSign = TChar( '-' );
+    
+    NormalizeValue();
+    }
+
+
+TDigitType CAknMfneFloat::DigitType() const
+    {
+    return iDigitType;
+    }
+    
+        
+void CAknMfneFloat::SetFlags( TUint aFlags )
+    {
+    iFlags = aFlags;
+    NormalizeValue();
+    }
+    
+TUint CAknMfneFloat::Flags() const
+    {
+    return iFlags;
+    }