diff -r 000000000000 -r 2f259fa3e83a uifw/eikctl/src/aknmfnefloat.cpp --- /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 +#include +#include + +#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 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; + }