/*
* Copyright (c) 2006 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:  An implementation of a configurable key translation map.
*
*/


// INCLUDES
#include "kefmapimpl.h"
#include <e32std.h>
#include <e32math.h>
#include <barsc.h>
#include <barsread.h>
#include <bautils.h>
#include "kefresource.h"
#include <aknkeyeventmap.rsg>
#include "keflogger.h"
#include "kefprovider.h"

// CONSTANTS
#define STRIP_MODS  &0x0000FFFF
#define GET_MODS    &0xFFFF0000

// Key event map resource file.
_LIT( KKefKeyTableRsc, "z:\\resource\\AknKeyEventMap.rsc" );

// Publish & Subscribe device mode category.
const TInt32 KUidWinservCategoryValue = 0x10003B20;

// Publish & Subscribe device mode category as TUid.
const TUid KUidWinservCategory = { KUidWinservCategoryValue };

// Always pass policy.
_LIT_SECURITY_POLICY_PASS(KAlwaysPassPolicy);

// Only system application can access.
_LIT_SECURITY_POLICY_S0(KSysapOnlyPolicy, 0x100058F3);

// ============================ MEMBER FUNCTIONS ===============================


// -----------------------------------------------------------------------------
// CKefMapImpl::NewL
// -----------------------------------------------------------------------------
//
CKefMapImpl* CKefMapImpl::NewL()
    {
    CKefMapImpl* self = new (ELeave)CKefMapImpl();
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::CKefMapImpl
// -----------------------------------------------------------------------------
//
CKefMapImpl::CKefMapImpl()
    : iDownEvents( KKefMaxComboKeys ), iUpEvents( KKefMaxComboKeys )
    {    
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::ConstructL
//
// Initializes member variables, reads the default keytables from a resource 
// file, instantiates P&S subscriber and opens the door for raw events.
// -----------------------------------------------------------------------------
//
void CKefMapImpl::ConstructL()
    {

    if ( KKefMaxDeviceMode < 1 || KKefMaxDeviceMode > 15)
        {
        __KEFLOGSTRING("CKefMapImpl::ConstructL ERROR: must have at least 1 and at most 15 device modes");
        User::Leave(KErrGeneral);
        }
    //NEW CODE
    iDelayedCombo = EFalse;
    iIsPartialMatch = EFalse;
    iCompleteDelayedComboMatch = EFalse;
    iPostPendingDownEvents = EFalse;
    //END NEW CODE
    iCurrentDeviceMode = KKefModeDefault;
    iKeyBeacon.iKeyCode = EKeyNull;
    iKeyBeacon.iEventType = 0;
    iDownTimer = CPeriodic::New( CActive::EPriorityLow );
    iCombos = new (ELeave) RArray<RArray<TUint32>*>( KKefMaxComboKeys );
    
    iRepeatRate = KKefRepeatRate;
    iKeyPollInterval = static_cast<TTimeIntervalMicroSeconds32>(1000000./iRepeatRate);
    
    iConsume = EFalse;
    iIsCanceled = EFalse;
    
    iScanCodeIJustGenerated = -1;
    iPostedDelayedScanCode = -1;
    
    iIsCombo = EFalse;
    
    // Initialize keymap.
    const TKefKeyEvent nullEvent = { EKeyNull, EKeyNull, 0 };
    for ( TUint j = 0; j < KKefKeyMapRows; j++ )
        {
        for ( TUint i = 0; i < KKefKeyMapColumns; i++ )
            {
            iKeyMap[i][j] = nullEvent;
            }
        }
    
    // Load the default configuration.
    TRAPD( ierr, InitializeKeyTableFromResourceL( KKefKeyTableRsc ) );
    if ( ierr != KErrNone )
        {
        __KEFLOGSTRING1( "Error initializing the key tables, %d", ierr );
        }

    if ( !IsKeyEventMapEnabled() )
        {
        return;
        }
    
    // Define Publish & Subscribe key
    TInt err = RProperty::Define( KUidWinservCategory, KKefPSDeviceMode, 
        RProperty::EInt, KAlwaysPassPolicy, KSysapOnlyPolicy );
    if ( err != KErrNone )
        {
        __KEFLOGSTRING1("CKefMapImpl::ConstrucL() Problem defining The Key %d", err);
        }

    // Subscribe to Publish & Subscribe keys
    err = KErrNone;
    CKefSubscriber *sub = NULL;
    
    sub = CKefSubscriber::NewL( *this, KUidWinservCategory , KKefPSDeviceMode  );
    if ( sub )
        {        
        err = iKefSubscribers.Append( sub );
        if ( err != KErrNone )
            {
            delete sub;
            __KEFLOGSTRING( "CKefMapImpl::ConstructL ERROR: device mode subscription failed." );
            // We could stop the boot here, but we can also continue, just without 
            // the device modes.
            }
        }
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::~CKefMapImpl
// -----------------------------------------------------------------------------
//
CKefMapImpl::~CKefMapImpl()
    {
    __KEFLOGSTRING("CKefMapImpl::Destructor entered.");
    
    if ( iCombos )
        {
        const TInt count = iCombos->Count();
        for ( TInt i = count - 1; i >= 0; i-- ) 
            {
            (*iCombos)[i]->Close();
            delete (*iCombos)[i];
            iCombos->Remove(i);
            }
        iCombos->Close();
        delete iCombos;
        }
    
    delete iDownTimer;

    iUpEvents.Close();   
    iDownEvents.Close();
    iRepeatRates.Close();
    
    iKefSubscribers.ResetAndDestroy();
    iKefSubscribers.Close();
    
    iRawEventsNotPosted.Close();
    }


// -----------------------------------------------------------------------------
// CKefMapImpl::SetProvider
// -----------------------------------------------------------------------------
//    
void CKefMapImpl::SetProvider( MKefProvider& aProvider )
    {
    iProvider = &aProvider;
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::IsKeyEventMapEnabled
// -----------------------------------------------------------------------------
//
TBool CKefMapImpl::IsKeyEventMapEnabled() const
    {
    return iKeyEventMapEnabled;
    }
    
// -----------------------------------------------------------------------------
// CKefMapImpl::OfferRawEvent
// 
// This is where the raw events end up in, and
// where we have to decide what to do with them.
// -----------------------------------------------------------------------------
//
TBool CKefMapImpl::OfferRawEvent( const TRawEvent& aRawEvent, TBool aSilentEvent )    
    {
    __ASSERT_ALWAYS( iProvider, User::Invariant() );
    
    const TBool downEvent = ( aRawEvent.Type() == TRawEvent::EKeyDown );
    const TBool upEvent = ( aRawEvent.Type() == TRawEvent::EKeyUp );
    //NEW CODE
    iCompleteDelayedComboMatch = EFalse;
    //END NEW CODE
    //
    // Raw events created by this CKefMapImpl itself: pass them on.
    //
    if ( ( downEvent || upEvent ) && 
         ( aRawEvent.ScanCode() == iScanCodeIJustGenerated ) )
        {
        if ( downEvent )
            {
            // Add this scan code to iUpEvents so we can generate
            // the appropriate raw UPs when the key press is finished.
            __KEFLOGSTRING1("CKefMapImpl::OfferRawEvent adding %x to iUpEvents", iScanCodeIJustGenerated);
            TInt err = iUpEvents.Append( iScanCodeIJustGenerated );
            if ( err != KErrNone )  // never happened
                {
                __KEFLOGSTRING("CKefMapImpl::OfferRawEvent ERROR appending up event!");
                }        
            }
    
        if ( upEvent )
            {
            // would be nice to .Remove() elements from iUpEvents here,
            // but that'd require another call to ResolveMapping(),
            //  -- .Remove() in CancelProcessingKeyPress() instead.
            }
            
        iScanCodeIJustGenerated = -1;
        return EFalse;  // not further processing for scan codes iJustGenerated.
        } 
    
    //NEW CODE
    if ( ( downEvent || upEvent ) && 
         ( aRawEvent.ScanCode() == iPostedDelayedScanCode ) )
        { 
        iPostedDelayedScanCode = -1;
        return EFalse;  // not further processing for scan codes iJustGenerated.
        }
    //END NEW CODE      
        
    TInt err = KErrNone;
    iConsume = EFalse;  // very important to set this here.
    
    // log arrays
#ifdef _KEF_LOGGING	
    for ( TInt i = 0 ; i < iDownEvents.Count() ; i++ )
        {
        __KEFLOGSTRING2("CKefMapImpl::OfferRawEvent iDownEvents[%d] = %x", i, iDownEvents[i] );
        }
    for ( TInt i = 0 ; i < iUpEvents.Count() ; i++ )
        {
        __KEFLOGSTRING2("CKefMapImpl::OfferRawEvent iUpEvents[%d] = %x", i, iUpEvents[i] );
        }
#endif // _KEF_LOGGING	
    
    // Down events
    if ( downEvent && aRawEvent.ScanCode() >= 0 )
        {
        iIsDownEvent = ETrue;
        iCurrentScanCode = aRawEvent.ScanCode(); 
        iPostMeOnUp.iKeyCode = EKeyNull;
        iPostMeOnUp.iEventType = 0;
        __KEFLOGSTRING3( "CKefMapImpl::OfferRawEvent KeyDown scancode=%x mods=%x, we're in devicemode=%d", 
            iCurrentScanCode STRIP_MODS, iCurrentScanCode GET_MODS, iCurrentDeviceMode);
        
        // Combo support
        if ( iDownEvents.Count() < KKefMaxComboKeys )
            {
            err = iDownEvents.Append( aRawEvent.ScanCode() STRIP_MODS );
            if ( err != KErrNone )  // never happened
                {
                __KEFLOGSTRING("CKefMapImpl::OfferRawEvent ERROR appending down event!");
                }                
            }
        else 
            {
            // User is trying to press more simultaneous keys than supported.
            // We have no problem with that, just ignore it.
            }
        
        // Resolve mapping for this event and set is as the beacon.
        iKeyBeacon = ResolveMapping();

        // Launch a CPeriodic so the duration of the keypress can be known.  
        // There's only one timer; for combos, it measures the time starting
        // from the first DOWN event of the combo.  If usability troubles appear,
        // try re-starting the timer for each participating key in a combo
        // (by removing the test against iDownEvents.Count())
        //NEW CODE
        if ( iDownEvents.Count() < 2 && !iDelayedCombo)
        //END NEW CODE        
            {            
            iKeyPressDuration = 0.0;
            iDownTimer->Cancel();
            iIsCanceled = EFalse; // set this before Start()
            iDownTimer->Start( iKeyPollInterval, iKeyPollInterval, 
                TCallBack( &KeyPressDuration, (TAny*) this ) );
            }

        if ( iKeyBeacon.iKeyCode > KKefKeyDisabled )
            {
            if ( ( iKeyBeacon.iEventType & KKefIsLongOnly ) )
                {
                // delayed posting of the short event when KKefIsLongOnly
                iPostMeOnUp = iKeyBeacon;  
                return iConsume;      // notice!  we're finished with this event.
                }//this might be part of special combo key event
            //NEW CODE
            else if(iDelayedCombo && !iCompleteDelayedComboMatch)
                {
                    __KEFLOGSTRING("CKefMapImpl::OfferRawEvent Delayed Combo; downevent pending");
                 //delay posting of down event(s) in case this is part of the combo
                 return iConsume;      // notice!  we're finished with this event.   
                }
            //END NEW CODE
            else
                {
                if ( ( iKeyBeacon.iEventType & KKefIsShortKey ) == EFalse )
                    {
                    PostEvent( EKefPostRaw );
                    }
                else 
                    {
                    PostEvent( EKefPostKey );
                    }
                //NEW CODE    
                if(iCompleteDelayedComboMatch)
                    {
                        TInt existsAlready(-1);
                        __KEFLOGSTRING("CKefMapImpl::OfferRawEvent Delayed Combo Complete. Key event posted");
                      //Events for keys part of completed special combo are NOT posted. Ever.
                      for(TInt i=0; i<iDownEvents.Count(); i++)
                        {   
                            existsAlready = iRawEventsNotPosted.Find( iDownEvents[i] );
                            //However, do NOT add same events more than once.
                            //When user is pressing the second key of a combo multiple times and holding the first down
                            //we'd have the first key of the combo umpty times on the list.
                            if(existsAlready == KErrNotFound)
                                iRawEventsNotPosted.Append( iDownEvents[i] );
                        }
                            
                      iIsPartialMatch = EFalse;
                      iPostPendingDownEvents = EFalse;
                      iCompleteDelayedComboMatch = EFalse;
                    }
                //END NEW CODE  
                }
            }
         //NEW CODE
         if(iPostPendingDownEvents)
                {   //This is not a special combo. Post delayed downevents.
                    __KEFLOGSTRING("CKefMapImpl::OfferRawEvent Delayed Combo ABORT. Post pending events.");
                    iDelayedCombo = EFalse;
                    iIsPartialMatch = EFalse;
                    TInt lastBeforeCurrentEvent = iDownEvents.Count();
                    TInt eventNotPostedIndex(-1);
                    //The current downevent will be posted by the window server (iConsume is EFalse).
                    for(TInt i=0; i<lastBeforeCurrentEvent; i++)
                        {
                            eventNotPostedIndex = KErrNotFound;
                            if(iRawEventsNotPosted.Count())                           
                                eventNotPostedIndex = iRawEventsNotPosted.Find( iDownEvents[i] );
                            if(eventNotPostedIndex == KErrNotFound)
                                {
                                    
                                    iPostedDelayedScanCode = iDownEvents[i];
                                    TRawEvent rawEvent;
                                    rawEvent.Set( TRawEvent::EKeyDown, iPostedDelayedScanCode );
                                    __KEFLOGSTRING1("CKefMapImpl::OfferRawEvent Posted pending event scancode=%x",rawEvent.ScanCode());
                                    iProvider->KefPostRawEvent( rawEvent );
                                }
                        }          
                    iPostPendingDownEvents = EFalse;
                }
            //END NEW CODE       
        
        // Emit a keyclick.
        if ( !aSilentEvent && iKeyBeacon.iKeyCode != KKefKeyDisabled )
            {
            if ( iKeyBeacon.iKeyCode > KKefKeyDisabled )
                {
                if (iDownEvents.Count() > 1)
                	{	//Make sure the key sound is played for the last key of a combo
                		iProvider->KefGenerateKeySound( iCurrentScanCode STRIP_MODS );
                	}
                else
                	{	//Otherwise send the mapped single key
                		iProvider->KefGenerateKeySound( iKeyBeacon.iKeyCode STRIP_MODS );
                	}
                }
            else
                {
                iProvider->KefGenerateKeySound( aRawEvent.ScanCode() STRIP_MODS );
                }
            }
        
        return iConsume; // this was set by ResolveMapping
        }
    
    //  Up events    
    if ( upEvent )
        {
        iIsDownEvent = EFalse;               
        iCurrentScanCode = aRawEvent.ScanCode(); 
        __KEFLOGSTRING1("CKefMapImpl::OfferRawEvent KeyUp scancode=%x",aRawEvent.ScanCode());
        
        // Check whether this up event finishes a keypress we've been handling.
        TInt finishesKeyPress = 
            iDownEvents.Find( aRawEvent.ScanCode() STRIP_MODS );
        __KEFLOGSTRING1("CKefMapImpl::OfferRawEvent finishesKeypress index %d", finishesKeyPress);
        __KEFLOGSTRING1("CKefMapImpl::OfferRawEvent Duration =%f",iKeyPressDuration);
        
        // We got an UP for a known DOWN

        if ( finishesKeyPress != KErrNotFound) 
            {

                 
            if ( iPostMeOnUp.iKeyCode > KKefKeyDisabled && !iIsLongKeyPress  )
                {
                iKeyBeacon = iPostMeOnUp;
                if ( ( iKeyBeacon.iEventType & KKefIsShortKey ) == EFalse )
                    {
                    PostEvent( EKefPostRaw );
                    }
                else 
                    {
                    PostEvent( EKefPostKey );
                    }
                iPostMeOnUp.iKeyCode = EKeyNull;
                iPostMeOnUp.iEventType = 0;
                }
            
            //NEW CODE
            if(iDelayedCombo )
                {
                    TInt lastDownEventIndex = iDownEvents.Count() - 1;
                    TInt eventNotPostedIndex(-1);
                    TInt lastEvent = iDownEvents[lastDownEventIndex]; //DEBUG VARIABLE
                    //Up event for some other key than the last pressed; handling of special event aborted.
                    if(finishesKeyPress != lastDownEventIndex)
                        {
                            __KEFLOGSTRING("CKefMapImpl::OfferRawEvent: Not the last key of DK. ABORT by UP event.");
                            iDelayedCombo = EFalse;
                            iIsPartialMatch = EFalse;
                            iCompleteDelayedComboMatch = EFalse;
                            iPostPendingDownEvents = ETrue;
                            //Check if current UP event is in the "not to be posted" list
                            eventNotPostedIndex = iRawEventsNotPosted.Find( iCurrentScanCode STRIP_MODS );
                            if(eventNotPostedIndex != KErrNotFound)
                              {
                                iDownEvents.Remove( finishesKeyPress ); 
                                iRawEventsNotPosted.Remove(eventNotPostedIndex);
                                iConsume = ETrue; //This UP event is not sent either...
                              }
                            //Post pending down events
                            __KEFLOGSTRING("CKefMapImpl::OfferRawEvent: Post pending events.");
                            for(TInt i=0; i<iDownEvents.Count(); i++)
                                {
                                    eventNotPostedIndex = iRawEventsNotPosted.Find( iDownEvents[i] STRIP_MODS );
                                    //Event is not in the "not to be posted" list; send it.
                                    if(eventNotPostedIndex == KErrNotFound)
                                        {
                                            iPostedDelayedScanCode = iDownEvents[i];
                                            TRawEvent rawEvent;
                                            rawEvent.Set( TRawEvent::EKeyDown, iPostedDelayedScanCode );
                                            iProvider->KefPostRawEvent( rawEvent );
                                            eventNotPostedIndex = - 1;
                                        }
      
                                }
                                          
                            iPostPendingDownEvents = EFalse;
                        }
                     else //Last key of a special combo or a candidate.Continue processing as special combo.
                        {
                            __KEFLOGSTRING("CKefMapImpl::OfferRawEvent: The last key of DK.");
                            eventNotPostedIndex = iRawEventsNotPosted.Find( iCurrentScanCode STRIP_MODS );
                            //Last key of a partial match 
                            if(eventNotPostedIndex == KErrNotFound)
                                {
                                  __KEFLOGSTRING("CKefMapImpl::OfferRawEvent: Last key not on NO list. Post downevent.");
                                  iPostedDelayedScanCode = iDownEvents[finishesKeyPress];
                                  iDownEvents.Remove( finishesKeyPress ); 
                                  TRawEvent rawEvent;
                                  rawEvent.Set( TRawEvent::EKeyDown, iPostedDelayedScanCode );
                                  //Post DOWN event. UP event will be posted by Window Server since iConsume is False.
                                  iProvider->KefPostRawEvent( rawEvent );
                                }
                            else //Part of a completed special event. Do not post events associated with this key.
                                { 
                                    __KEFLOGSTRING("CKefMapImpl::OfferRawEvent: Last key on NO list. Don't post events.");  
                                  iDownEvents.Remove( finishesKeyPress ); 
                                  iRawEventsNotPosted.Remove(eventNotPostedIndex);
                                  iConsume = ETrue;
                                }  
   
                        }

                }
            else
                {
                     TInt eventNotPostedIndex = iRawEventsNotPosted.Find( aRawEvent.ScanCode() STRIP_MODS );
                     //This has been part of a sent special combo; do not send raw event.
                     if(eventNotPostedIndex != KErrNotFound)
                         {
                           iRawEventsNotPosted.Remove(eventNotPostedIndex);
                           iDownEvents.Remove( finishesKeyPress );
                           iConsume = ETrue;
                         }
                 //END NEW CODE
                     else //Business as usual.
                        {
                            //
                            iKeyBeacon = ResolveMapping();
                            // Remove key from the down array
                            iDownEvents.Remove( finishesKeyPress );
                            
                            // if it is mapped on the up array -> generate the up event
                            TInt upKeyPressIndex = KErrNotFound;
                            for ( TInt i = 0 ; i < iUpEvents.Count() ; i++ )
                                {
                                if ( (iUpEvents[i] STRIP_MODS) == ( iKeyBeacon.iKeyCode STRIP_MODS ) )
                                    {
                                    upKeyPressIndex = i;
                                    break;
                                    }
                                }
                            
                            if ( upKeyPressIndex != KErrNotFound )
                                {
                                iScanCodeIJustGenerated = iUpEvents[upKeyPressIndex];
                                TRawEvent rawEvent;
                                rawEvent.Set( TRawEvent::EKeyUp, iScanCodeIJustGenerated );
                                __KEFLOGSTRING1("CKefMapImpl::OfferRawEvent POST RAW UP-EVENT for %x",iScanCodeIJustGenerated);
                                iProvider->KefPostRawEvent( rawEvent ) ;
                                iUpEvents.Remove(upKeyPressIndex);
                                iConsume = ETrue;
                                }
                        }
                        
                }
                
                    
            if ( !iDownEvents.Count() )
                 {
                  CancelProcessingKeyPress();
                 }

            }
         else // finishesKeyPress == KErrNotFound
            {

                    // probably a hardware/driver error, in any case we're
                    // confused now.
                    __KEFLOGSTRING("CKefMapImpl::OfferRawEvent INPUT IS CONFUSED: cancel key processing");            
                    CancelProcessingKeyPress();     
            }
        }
    
    return iConsume;
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::CancelProcessingKeyPress
// -----------------------------------------------------------------------------

void CKefMapImpl::CancelProcessingKeyPress()
    {
    iDownTimer->Cancel();
    iIsCanceled = ETrue;
    iDownEvents.Reset();
    iRawEventsNotPosted.Reset();
    
    // be sure to send out all raw upevents or CKeyTranslator
    // will think the key is still being pressed.
    TRawEvent rawEvent;    
    while ( iUpEvents.Count() > 0 )
        {
        iScanCodeIJustGenerated = iUpEvents[0];
        rawEvent.Set( TRawEvent::EKeyUp, iScanCodeIJustGenerated );
        __KEFLOGSTRING1("CKefMapImpl::CancelProcessingKeyPress POST RAW UP-EVENT for %x",iScanCodeIJustGenerated);
        iProvider->KefPostRawEvent( rawEvent ) ;
        iUpEvents.Remove(0);
        }
        
    iUpEvents.Reset();
    iRepeats = 0;
    iKeyPressDuration = 0.0;
    iIsLongKeyPress = EFalse;                
    __KEFLOGSTRING1("CKefMapImpl::CancelProcessingKeyPress Last beacon sent: %x", iKeyBeacon.iKeyCode);
    iKeyBeacon.iKeyCode = EKeyNull;
    iKeyBeacon.iEventType = 0;
    //NEW CODE
    iDelayedCombo = EFalse;
    iIsPartialMatch = EFalse;
    iCompleteDelayedComboMatch = EFalse;            
    iPostPendingDownEvents = EFalse;   
    //END NEW CODE                     
    }
    

// -----------------------------------------------------------------------------
// CKefMapImpl::HandlePropertyChangedL
//
// Callback for Publish & Subscribe key events, it tracks
// the device mode changes for internal use.
// -----------------------------------------------------------------------------
//
void CKefMapImpl::HandlePropertyChangedL( 
        const TUid& aCategory, 
        const TUint aKey )
    {
    TInt keyValue( 0 );
    TInt err = KErrNone;
    err = RProperty::Get( aCategory, aKey, keyValue );
    if ( err == KErrNone )
        {
        switch( aKey )
            {
            case KKefPSDeviceMode:
                {
                __KEFLOGSTRING1("CKefMapImpl::HandlePropertyChanged VALUE: %d",aKey);
                iCurrentDeviceMode = keyValue;
                
                const TInt count = iRepeatRates.Count();
                TUint16 currentMode = iCurrentDeviceMode; 
                for ( TInt ii = 0; ii < count; ii++ )
                    {
                    const TKefKeyRepeatRate& repeatRate = iRepeatRates[ ii ];
                    
                    if ( ( currentMode & repeatRate.iModeMask ) == 
                         repeatRate.iMode )
                        {
                        SetRepeatRate( repeatRate.iRate );
                        ii = count; // exit the loop.
                        }
                    }
                }
                break;
            default:
                {
                __KEFLOGSTRING1("CKefMapImpl::HandlePropertyChanged GOT A P&S VALUE: %d",aKey);
                }
                break;
            }
        }
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::InitializeKeyTableFromResourceL
// -----------------------------------------------------------------------------
//
void CKefMapImpl::InitializeKeyTableFromResourceL(
        const TDesC& aConfigFile ) 
    {
    // Get a handle for the resource file.
    RFs fsSession;
    CleanupClosePushL( fsSession );
    TInt err = fsSession.Connect();
    if ( err != KErrNone )
        {
        User::Leave( err );
        }
    RResourceFile resourceFile;
    CleanupClosePushL( resourceFile );
    
    // Make sure we have the resource file.
    if ( BaflUtils::FileExists( fsSession, aConfigFile ) )
        {
        resourceFile.OpenL( fsSession, aConfigFile );
        }
    else
        {
        __KEFLOGSTRING( "CKefMapImpl::InitializeKeyTableFromResourceL: no configuration file!");
        CleanupStack::PopAndDestroy(); // CleanupClosePushL.
        CleanupStack::PopAndDestroy(); // CleanupClosePushL.
        return;
        }
    
    // Confirm signature of the resource file.
    resourceFile.ConfirmSignatureL( 0 );
    
    // Now just get the resource chunk into a heap buffer and give it to a 
    // TResourceReader.
    HBufC8* res;
    res = resourceFile.AllocReadLC( R_AVKON_KEY_EVENT_MAP );
    
    TResourceReader theReader;
    theReader.SetBuffer( res );
    
    // Check version field.
    const TUint16 version = theReader.ReadUint16();
    if ( version != KKefKeyEventVersion1 )
        {
        __KEFLOGSTRING( "CKefMapImpl::InitializeKeyTableFromResourceL: key event map disabled!");
        CleanupStack::PopAndDestroy( res );
        CleanupStack::PopAndDestroy(); // CleanupClosePushL.
        CleanupStack::PopAndDestroy(); // CleanupClosePushL.
        return;
        }
    
    iKeyEventMapEnabled = ETrue;
    theReader.ReadUint16(); // read flags, not in use yet.

    // Read repeat rates.
    const TUint count = theReader.ReadUint16();
    for ( TInt idx = 0; idx < count; idx++ )
        {
        TUint16 mode = theReader.ReadUint16();
        TUint16 modeMask = theReader.ReadUint16();
        TUint8 rate = theReader.ReadUint8();
        
        const TKefKeyRepeatRate repeatRate = 
            {
            mode,
            modeMask,
            rate
            };
        
        User::LeaveIfError( iRepeatRates.Append( repeatRate ) );
        }

    // Parses the resource data
    CKefResourceArray* keyResourceArray = CKefResourceArray::NewLC();
    CArrayPtrFlat<CKefResource>* keyEvents = NULL;
    keyEvents = keyResourceArray->GetKeyEventsL( theReader );
    
    // At this point we have all the key event info; just put it in its place
    // in iKeyMap and iCombos.
    for ( TUint i = 0; i < (TUint)keyEvents->Count(); i++ )
        {
        CKefResource* keyEvent = keyEvents->At(i);
        
        if ( keyEvent->GetScanCodes().Count() > 0 )
            {
            if ( (keyEvent->GetKeyCodes().Count() == keyEvent->GetDeviceModes().Count())
                 && (keyEvent->GetKeyCodes().Count() == keyEvent->GetEventTypes().Count()))
                {
                for ( TUint j = 0; 
                      j < (TUint)keyEvent->GetKeyCodes().Count(); 
                      j++ )
                    {            
                    TUint16 thisMode = keyEvent->GetDeviceModes().At(j);
                    RArray<TUint> theseModes;
                    CleanupClosePushL( theseModes );
                    
                    ResolveDeviceModeL( 
                        theseModes, 
                        keyEvent->GetDeviceModes().At(j) );
                    
                    TUint16 thisType = keyEvent->GetEventTypes().At(j);
                    for ( TUint k = 0; k < (TUint)theseModes.Count(); k++ )
                        {
                        TKefKeyDefinition thisKey;
                        thisKey.iScanCodes = &(keyEvent->GetScanCodes());
                        thisKey.iDeviceMode = theseModes[k];
                        thisKey.iKeyCode = keyEvent->GetKeyCodes().At(j);
                        thisKey.iEventType = thisType;
#ifdef RD_TACTILE_FEEDBACK
                        thisKey.iFeedbackType = keyEvent->GetFeedbackType();
#endif // RD_TACTILE_FEEDBACK
                        
                        TRAPD( error, SetKeyMappingL( thisKey ) );
                        if (error != KErrNone)
                            {
                            __KEFLOGSTRING( "CKefMapImpl::InitializeKeyTableFromResourceL ERROR setting a mapping");
                            }
                        }
                    
                    CleanupStack::PopAndDestroy( &theseModes );
                    }
                }
            else
                {
                // Keyboard configuration is erroneous!
                // When multiple keycodes have been defined, they must have
                // an associated device mode, etc.
                __KEFLOGSTRING( "CKefMapImpl::InitializeKeyTableFromResourceL ERROR: inconsistent data in KEY_EVENT" );
                User::Leave( KErrGeneral );
                }
            }
        else 
            {
            // Error. No scancodes for the key event.
            User::Leave( KErrGeneral );
            }    
        }
    
    // All done! The maps are now ready for use!
    CleanupStack::PopAndDestroy( 4 ); // keyResourceArray, res, 
                                      // &resourceFile, &fsSession
    }
    
// -----------------------------------------------------------------------------
// CKefMapImpl::SetRepeatRate
// -----------------------------------------------------------------------------
//
void CKefMapImpl::SetRepeatRate( TUint8 aRepeatRate )
    {
    if ( iRepeatRate > 0 && iRepeatRate < 32 )
        {        
        iRepeatRate = aRepeatRate;
        }
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::SetKeyMappingL
//
// Sets the keycodes in iKeyMap and iCombos for one device mode.
// -----------------------------------------------------------------------------
//
void CKefMapImpl::SetKeyMappingL( const TKefKeyDefinition& aKey )
    {
    if ( aKey.iScanCodes->Count() == 1 )
        {        
        // Normal (=single key) mapping
        if ( (aKey.iEventType & KKefTypeShort) == KKefTypeShort )
            {
            // handle anykey definition for single presses.
            if ( aKey.iScanCodes->At(0) == KKefAnyKey)
                {
                for (TUint j = 0; j < KKefMaxScanCode; j++) 
                    {
                    iKeyMap[j][aKey.iDeviceMode].iShortPress = 
                        aKey.iKeyCode;
                    iKeyMap[j][aKey.iDeviceMode].iLongPress = 
                        aKey.iKeyCode;
                    iKeyMap[j][aKey.iDeviceMode].iEventType |=
                        ResolveEventType ( aKey.iEventType );
#ifdef RD_TACTILE_FEEDBACK
                    iKeyMap[j][aKey.iDeviceMode].iFeedbackType = 
                        aKey.iFeedbackType;
#endif // RD_TACTILE_FEEDBACK
                    }
                }
            else 
                {                                
                // default is to set iLongPress to same as iShortPress, which in 
                // practice means that the default behavior for long keypresses is
                // to repeat.
                iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iShortPress = 
                    aKey.iKeyCode;
                iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iLongPress = 
                    aKey.iKeyCode;
                iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iEventType |= 
                    ResolveEventType( aKey.iEventType );
#ifdef RD_TACTILE_FEEDBACK
                iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iFeedbackType = 
                    aKey.iFeedbackType;
#endif // RD_TACTILE_FEEDBACK
                }
            }
        else if ( ((aKey.iEventType & KKefTypeLong) == KKefTypeLong) ||
                  ((aKey.iEventType & KKefTypeLongOnly) == KKefTypeLongOnly))
            {
            iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iLongPress  = 
                aKey.iKeyCode;
            iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iEventType |= 
                ResolveEventType( aKey.iEventType );
#ifdef RD_TACTILE_FEEDBACK
            iKeyMap[aKey.iScanCodes->At(0)][aKey.iDeviceMode].iFeedbackType = 
                aKey.iFeedbackType;
#endif // RD_TACTILE_FEEDBACK
            }
        else
            {
            // Ignored.
            }
        }
    else
        {
        // Combo mapping
        RArray<TUint32>* newCombo = 
            new (ELeave) RArray<TUint32>( KKefMaxComboKeys );
        TUint i;
        TInt err = KErrNone;
        for ( i = 0; i < aKey.iScanCodes->Count(); i++ ) 
            {           
            err = newCombo->Append( aKey.iScanCodes->At(i) );
            if ( err != KErrNone ) 
                {
                User::Leave( err );  // we're trapped, just bail out.
                }
            }
        
        // For combos, the device mode information is encoded in to the
        // upper 16 bits of the Key Code value.    
        TUint32 keyMode = aKey.iKeyCode + ( aKey.iDeviceMode << 16 );
        newCombo->Insert( keyMode, 0 );
       
        TUint32 eventType = ResolveEventType( aKey.iEventType );
        newCombo->Insert( eventType, 1 );

        err = iCombos->Append( newCombo );
        if ( err != KErrNone )
            {
            newCombo->Close();
            delete newCombo;
            User::Leave( err );
            }  
        }
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::ResolveEventType
//
// A conversion between the eventtype representation in the resource file
// and iKeyMap.
// -----------------------------------------------------------------------------

TUint8 CKefMapImpl::ResolveEventType( TUint8 aEventType )
    {
    TUint8 result = 0;
    
    if ( (aEventType & (KKefTypeLongOnly)) == KKefTypeLongOnly )
        {
        result |= KKefIsLongOnly;
        result |= KKefIsLongPress;
        if ( (aEventType & KKefTypeKey) )
            {
            result |= KKefIsLongKey;
            }
        }
    else if ( (aEventType & KKefTypeLong) == KKefTypeLong )
        {
        result |= KKefIsLongPress;
        if ( (aEventType & KKefTypeKey) == KKefTypeKey )
            {
            result |= KKefIsLongKey;
            
            // This is needed if you want continious events from your
            // combo mapping, i.e. hold down the modifier and press
            // the other key several times to obtain the wanted result
            //
            if ( (aEventType & KKefTypeShort) == KKefTypeShort )
                {
                result |= KKefTypeShort;
                }
            
            }
        
        }
    else if ( (aEventType & KKefTypeShort) == KKefTypeShort )
        {
        result = 0; // to be 101% sure
        if ( (aEventType & KKefTypeKey) == KKefTypeKey )
            {
            result |= KKefIsShortKey;
            //NEW CODE
            //In case this is a special combo event.
            if ( (aEventType & KKefTypeDelayedCombo) == KKefTypeDelayedCombo )
            	{
            	result |= KKefIsDelayedOnCombo;
            	}
            //END NEW CODE
            }
        }

    return result;
    }
    
// -----------------------------------------------------------------------------
// CKefMapImpl::ResolveDeviceModeLC
//
// Given a TUint specifying a device mode, this function figures out 
// which rows of the iKeyMap are affected by a mapping. Only used in 
// setting up the key tables, not when querying data from them (just use 
// iCurrentDeviceMode then).
// -----------------------------------------------------------------------------
//
void CKefMapImpl::ResolveDeviceModeL( 
        RArray<TUint>& aModes, 
        TUint16 aMode )
    {
    TInt err;   
    TUint16 myMode = aMode;
    
    for ( TUint k = 0; k < KKefKeyMapRows; k++ )
        {
        // The loop index is now tested against aMode to see whether
        // there should be a non-null value in iKeyMap.
        
        if ( k == myMode )
            {            
            err = aModes.InsertInOrder( k );
            if ( err != KErrNone && err != KErrAlreadyExists )
                {
                User::Leave( err );  // we can't initialize the table.  fail.
                }
            }
        }
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::GetKeyMapping
//
// Returns mappings from iKeyMap.
// -----------------------------------------------------------------------------
//
TKefKeyBeacon CKefMapImpl::GetKeyMapping( 
        TUint16 aScanCode, 
        TUint16 aDeviceMode )
    {
#ifndef RD_TACTILE_FEEDBACK
    TKefKeyBeacon mapValue = { EKeyNull, 0 };
#else
    TKefKeyBeacon mapValue = { EKeyNull, 0, 0 };
#endif // RD_TACTILE_FEEDBACK

        if ( aScanCode < KKefKeyMapColumns && aDeviceMode < KKefKeyMapRows )
            {
            // Look up the mode specific value, if any
            if ( iIsLongKeyPress == EFalse )
                {
                mapValue.iKeyCode = iKeyMap[aScanCode][aDeviceMode].iShortPress;
                }
            else
                {
                mapValue.iKeyCode = iKeyMap[aScanCode][aDeviceMode].iLongPress;
                }

            mapValue.iEventType = iKeyMap[aScanCode][aDeviceMode].iEventType;        
#ifdef RD_TACTILE_FEEDBACK
            mapValue.iFeedbackType = iKeyMap[aScanCode][aDeviceMode].iFeedbackType;
#endif // RD_TACTILE_FEEDBACK
                
            // With this, we always return the default mapping (if any)
            // unless a mode-specific mapping was found.  
            if ( !mapValue.iKeyCode )
                {
                if ( iIsLongKeyPress == EFalse )
                    {
                    mapValue.iKeyCode = iKeyMap[aScanCode][ KKefModeDefault ].iShortPress; 
                    }
                else
                    {
                    mapValue.iKeyCode = iKeyMap[aScanCode][ KKefModeDefault ].iLongPress;
                    }
                mapValue.iEventType = iKeyMap[aScanCode][ KKefModeDefault ].iEventType;        
#ifdef RD_TACTILE_FEEDBACK
                mapValue.iFeedbackType = iKeyMap[aScanCode][KKefModeDefault].iFeedbackType;
#endif // RD_TACTILE_FEEDBACK
                }
            }

    return mapValue;
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::GetComboMapping
//
// Returns mappings from iCombos.
// -----------------------------------------------------------------------------
//
TKefKeyBeacon CKefMapImpl::GetComboMapping( 
        RArray<TUint32>& aComboCandidate )
    {
    TKefKeyBeacon mapValue = { EKeyNull, 0 };        
    
    TUint i;
    TUint j;
    //NEW CODE
    TUint noOfMatches(0);
    TBool partialMatch(EFalse);
    //END NEW CODE
    
    // First we check the first index of aComboCandidate
    // against the second index (= first scancode) of all combo
    // arrays in iCombos. If they match, then the i'th combo
    // array in iCombos could match aComboCandidate.
    //
    // iCombos[i][0] contains the key code that is to be returned for a 
    // succesful mapping, which introduces the offset-by-one in the following 
    // loops/lookups.also included in [i][0] are the device mode info and the 
    // event type info, both encoded into the upper 16 bits of iCombos[i][0].
    
    RArray<TUint> possibleMatch;  //  iCombos indices that might match
    for (i = 0; i < iCombos->Count(); i++) 
        {
        if ( (*(*iCombos)[i])[2] == aComboCandidate[0])
            {
            if((((*iCombos)[i])->Count()-2) == aComboCandidate.Count() )
                possibleMatch.Append(i);
            //NEW CODE
            //This might still be a potential match...
            if(iIsDownEvent)
                {
                    if(iDelayedCombo && (((((*iCombos)[i])->Count()-2) > aComboCandidate.Count())))
                        possibleMatch.Append(i);
                }           
            //END NEW CODE
            __KEFLOGSTRING1("CKefMapImpl::GetComboMapping ComboCandidate might hit iCombos[%d]",i);
            }                    
        }
    
    // If we have possibleMatches, then we just check whether any of
    // them matches aComboCandidate precisely. For the first match,
    // we return the keyvalue from the first element of the matching
    // combo array.
    if ( possibleMatch.Count() )
        {
        TInt8 isAMatch = -1;
        for ( i = 0; i < possibleMatch.Count() && isAMatch < 0; i++ ) 
            {
            // If isAMatch doesn't go to -1 within this loop, 
            // then we got a match.
            isAMatch = possibleMatch[i]; 
            for ( j = 2; j < (*(*iCombos)[possibleMatch[i]]).Count(); j++ ) 
                {
                // Try to find all scan codes of the current event 
                // in a combo definition.
                // NOTE: the order of the remaining scancodes is not matched,
                // as long as the first scancode is OK, the others can come in 
                // any order.
                TInt err = KErrNotFound;
                for ( TInt f = (*(*iCombos)[possibleMatch[i]]).Count()-1; f >= 0 ; f-- )
                    {
                    if ( (*(*iCombos)[possibleMatch[i]])[f] == aComboCandidate[j-2] )
                        {
                        err = f;
                        //NEW CODE
                        noOfMatches++; //record each hit
                        //END NEW CODE
                        break;
                        }
                    }
                
                
                // err < 2 must be discarded, because the first two elements are
                // used for device mode & event type data.
                if ( err == KErrNotFound || err < 2)
                    {
                    // If a scan code cannot be found, this possibleMatch is 
                    // not a real match.
                    isAMatch = -1;
                    //NEW CODE
                    noOfMatches --; //remove this "hit".
                    //END NEW CODE
                    }
                    //NEW CODE
                if ((j-2) == aComboCandidate.Count())
                    {
                        if(noOfMatches == aComboCandidate.Count())
                            partialMatch = ETrue;
                        break;//can happen when looking for a partial match as we took in combos
                          //with more scancodes than in the candidate array. Must exit or we go out of bounds
                          // of the candidate arrray.
                    }
                    //END NEW CODE
                }
            
            // If a matching combination of keys has been 
            // found we need to check that the combo type (long/short) and 
            // the active device mode for this combo match those of the current
            // event.

              TUint16 comboMode = ((*(*iCombos)[possibleMatch[i]])[0] GET_MODS) >> 16;
              TUint16 comboType = ((*(*iCombos)[possibleMatch[i]])[1]);

            
            
            // If ..
            //    a matching set of scan codes (or a partial match) was found,
            //    but it fails the long/short test,
            //    or it's defined for another device mode (default is always accepted).
            if ( isAMatch > -1 && 
                 ( iIsLongKeyPress && (!(comboType & KKefIsLongPress)  ) || 
                   ( ( iCurrentDeviceMode != comboMode ) && ( comboMode != KKefModeDefault ) ) ) )
                {
                isAMatch = -1;
                partialMatch = EFalse; //This wasn't even a partial match after all...
                }
            
            }
        
        if ( isAMatch > -1  && isAMatch < iCombos->Count() )
            {
            // The correct return value can be found from the isAMatch'th 
            // combo-mapping's first element.
            mapValue.iKeyCode = (*(*iCombos)[isAMatch])[0] STRIP_MODS;
            mapValue.iEventType = (*(*iCombos)[isAMatch])[1];
            __KEFLOGSTRING1("CKefMapImpl::GetComboMapping ComboCandidate DID hit iCombos[%d]", isAMatch);
            __KEFLOGSTRING1("CKefMapImpl::GetComboMapping mapValue = %d", mapValue.iKeyCode);
            partialMatch = EFalse; //Since complete match was found there's no need for a partial match, even if one was found.
            }
        }
    else 
        {
        mapValue.iKeyCode = EKeyNull;
        mapValue.iEventType = 0 ;
        }
    possibleMatch.Close();
    //NEW CODE
    iIsPartialMatch = partialMatch;
    //END NEW CODE
    return mapValue;
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::ResolveMapping
// 
// A little macro for figuring out how to map the current scan code.  
// -----------------------------------------------------------------------------
//
TKefKeyBeacon CKefMapImpl::ResolveMapping()
    {
    TKefKeyBeacon keyBeacon = 
    {
        EKeyNull, 0 
#ifdef RD_TACTILE_FEEDBACK
        , 0
#endif // RD_TACTILE_FEEDBACK
    };
    
    if ( iDownEvents.Count() > 1 && iDownEvents.Count() <= KKefMaxComboKeys ) 
        {
        // check that the current key is not disabled in this mode
        // if it is, then any combo containing this key must be 
        // blocked.
        TKefKeyBeacon tmp = GetKeyMapping( 
            iCurrentScanCode STRIP_MODS, iCurrentDeviceMode);
        
        if (tmp.iKeyCode != KKefKeyDisabled)
            {
            
            //Some products have two keys mapped to same scancode. It's not much use to try to check for combo
            //for two identical downevents as we'd just send the first potential combo event in the combo event list.    
            if(iDownEvents[0] != iDownEvents[1]) 
                {
                    keyBeacon = GetComboMapping( iDownEvents );
                }
           

            
            if ( (keyBeacon.iKeyCode == EKeyNull))
                {
                //NEW CODE 
                if((iDelayedCombo && !iIsPartialMatch) || !iDelayedCombo)
                    {                       
                //END NEW CODE 
                       __KEFLOGSTRING( "CKefMapImpl::ResolveMapping Combo not found. Trying to get single." );
                       keyBeacon = GetKeyMapping( iCurrentScanCode STRIP_MODS, iCurrentDeviceMode );
                //NEW CODE 
                    if(iDelayedCombo)
                        {
                            iDelayedCombo = EFalse;
                            iPostPendingDownEvents = ETrue; //spec combo has been aborted; send all pending down events.
                        }
                       
                    }
                 iCompleteDelayedComboMatch = EFalse;
                 
                }
            else 
                {   //Combo found; if the flag is up then a special event is ready.
                
                    iIsCombo = ETrue;
                    if(iDelayedCombo)
                        {
                            __KEFLOGSTRING("CKefMapImpl::ResolveMapping:handling Delayed Combo FOUND.");
                            iIsPartialMatch = EFalse; //lower this flag just to be sure
                            iCompleteDelayedComboMatch = ETrue; 
                        }
                       
                } 
               //END NEW CODE 
            }
        else
            {
            __KEFLOGSTRING("CKefMapImpl::ResolveMapping Ignoring combo because component is disabled");
            keyBeacon.iKeyCode = KKefKeyDisabled;
            keyBeacon.iEventType = 0;
            }
        }
    else if ( iDownEvents.Count() == 1 )
        { //NEW CODE                        
            iCompleteDelayedComboMatch = EFalse;
            //There's not much use to start searching for special combos if this is an UP event.
            if(iIsDownEvent)
                keyBeacon = GetFirstKey( iCurrentScanCode STRIP_MODS );
            
            if((keyBeacon.iKeyCode != EKeyNull) && (keyBeacon.iEventType & KKefIsDelayedOnCombo))
                {
                 __KEFLOGSTRING("CKefMapImpl::ResolveMapping:handling Delayed Combo first key.");
                 iDelayedCombo = ETrue;
                }
            else
           //END NEW CODE  //not the first key of a special combo, get normal mapping.
                keyBeacon = GetKeyMapping( iCurrentScanCode STRIP_MODS, iCurrentDeviceMode );
         
        }
    
    // Always consume succesful mappings, including EKeyDisabled
    if ( keyBeacon.iKeyCode != EKeyNull )
        {
        iConsume = ETrue;
        __KEFLOGSTRING1( "CKefMapImpl::ResolveMapping mapping found: %x", keyBeacon.iKeyCode );
        }
    else
        {
        __KEFLOGSTRING( "CKefMapImpl::ResolveMapping mapping not found" );
        }
    
    return keyBeacon;
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::KeyPressDuration
//
// Callback for iDownTimer, used to distinguish between short and 
// long key presses and to handle repeats.
// -----------------------------------------------------------------------------
//
TInt CKefMapImpl::KeyPressDuration( TAny* aSelf )
    {
    if (aSelf != NULL)
        {
        CKefMapImpl* self = reinterpret_cast<CKefMapImpl*>(aSelf);
        
        self->iKeyPressDuration += ((TReal32)self->iKeyPollInterval.Int())/1e6;
        
        // Distinguish between short and long key presses.
        if ( self->iKeyPressDuration < KKefKeyLongThreshold )
            {
            // nothing to do.  if combo duration measurement is changed
            // so that each participating key resets the timer, then
            // un-comment the following lines:
            // self->iIsLongKeyPress = EFalse;
            // self->iKeyBeacon = self->ResolveMapping();
            }
        else 
            {            
            // Keypress turned out to be a long one
            if ( ! self->iIsLongKeyPress && ! self->iIsCanceled ) 
                {
                self->iIsLongKeyPress = ETrue;
                self->iKeyBeacon = self->ResolveMapping();
    
                // post the KKefTypeLongOnly event
                if ( self->iKeyBeacon.iKeyCode > KKefKeyDisabled
                    && (self->iKeyBeacon.iEventType & KKefIsLongPress))
                    {
                    if ( (self->iKeyBeacon.iEventType & KKefIsLongKey ) ) 
                        {
                        self->PostEvent( EKefPostKey );
                        }
                    else  
                        {
                        if ( self->iKeyBeacon.iEventType & KKefIsLongOnly )
                            {
                            self->PostEvent( EKefPostRaw );
                            }
                        else
                            {
                            __KEFLOGSTRING("CKefMapImpl::KeyPressDuration - LONG PRESS JUST DETECTED; RAW repeat not sent (CKeyTranslator handles that)");
                            }
                        }
                    }
                }
            }
        
        // Handle repeats.
        if ( self->iKeyPressDuration >= KKefKeyLongThreshold && 
             self->iKeyBeacon.iKeyCode > KKefKeyDisabled ) 
            {
            if ( ( self->iKeyBeacon.iEventType & KKefIsLongKey ) )
                {
                //Make sure that the mapping is re-checked otherwise the iRepeats will grow until the last combo key is released
                TKefKeyBeacon oldBeacon(self->iKeyBeacon);
                
                // Call resolve mapping to check if one of the keys has been released
                self->iKeyBeacon = self->ResolveMapping();
                // If no change in mapping increase number of repeats
                // otherwise reset the iRepeats variable
                if ( self->iKeyBeacon.iKeyCode == oldBeacon.iKeyCode &&
                     self->iKeyBeacon.iEventType == oldBeacon.iEventType )
                    {
                    self->iRepeats++;    
                    }
                else
                    {
                    self->iRepeats = 0;
                    }
                  // These conditions needs to be re-checked, since iKeyBeacon may have changed above                     
                if ( self->iKeyBeacon.iKeyCode > KKefKeyDisabled
                    && (self->iKeyBeacon.iEventType & KKefIsLongPress))
                    {
                     if ( (self->iKeyBeacon.iEventType & KKefIsLongKey ) ) 
                        {
                        self->PostEvent( EKefPostKey );
                        }
                     else  
                        {
                        __KEFLOGSTRING("CKefMapImpl::KeyPressDuration - KKefIsLongKey = 1; RAW repeat not sent (CKeyTranslator handles that)");    
                        }
                        
                    }                    
                }
            else
                {
                __KEFLOGSTRING("CKefMapImpl::KeyPressDuration - KKefIsLongKey = 0; RAW repeat not sent (CKeyTranslator handles that)");   
                }
            }
        
        if ( self->iIsCanceled )
            {
            self->iDownTimer->Cancel();
            }
        }
    return KErrNone;
    } 

// -----------------------------------------------------------------------------
// CKefMapImpl::PostEvent
// -----------------------------------------------------------------------------

void CKefMapImpl::PostEvent(TUint aType) 
    {
    switch ( aType )
        {
        case EKefPostKey:
            TKeyEvent keyEvent;
            keyEvent.iCode = iKeyBeacon.iKeyCode;
            if(!iIsCombo)
                keyEvent.iScanCode = iCurrentScanCode STRIP_MODS;
            else// Discard the scan code in favour of the key code as combo events don't have a unabiguous scancode
                keyEvent.iScanCode = EStdKeyNull;
            keyEvent.iRepeats = iRepeats;
            keyEvent.iModifiers = iCurrentScanCode GET_MODS; 
            iProvider->KefPostKeyEvent(keyEvent);
#ifdef RD_TACTILE_FEEDBACK
            // tactile feedback requested in case of key events
            iProvider->KefGenerateFeedback( iKeyBeacon.iFeedbackType );
#endif // RD_TACTILE_FEEDBACK
            __KEFLOGSTRING1( "CKefMapImpl::PostEvent() POST KEY code=%x", keyEvent.iCode);
            iIsCombo = EFalse;
            break;        
        
        case EKefPostRaw:
        default:
            TRawEvent rawEvent;
            rawEvent.Set(TRawEvent::EKeyDown, (iCurrentScanCode GET_MODS) + iKeyBeacon.iKeyCode );
            iScanCodeIJustGenerated = rawEvent.ScanCode();
            __KEFLOGSTRING1( "CKefMapImpl::PostEvent() POST RAW scancode=%x", rawEvent.ScanCode());
            iProvider->KefPostRawEvent( rawEvent );
            break;
        }
    iConsume = ETrue;
    }
    
    
// -----------------------------------------------------------------------------
// KefMapFactory::CreateKefMapL
// Factory functoin to create instance out of CKefMap(Impl).
// -----------------------------------------------------------------------------
//
EXPORT_C CKefMap* KefMapFactory::CreateKefMapL()
    {
    return CKefMapImpl::NewL();
    }

// -----------------------------------------------------------------------------
// CKefMapImpl::GetFirstKey
// -----------------------------------------------------------------------------
//NEW CODE    
TKefKeyBeacon CKefMapImpl::GetFirstKey(TUint32 aComboFirstCodeCandidate )
    {
    TKefKeyBeacon mapValue = { EKeyNull, 0 };        
    
    TUint i;
    
    // We check the aComboCandidate
    // against the second index (= first scancode) of all combo
    // arrays in iCombos. If they match, then the i'th combo
    // array in iCombos could match aComboCandidate.
    //
    // iCombos[i][0] contains the key code that is to be returned for a 
    // succesful mapping, which introduces the offset-by-one in the following 
    // loops/lookups.also included in [i][0] are the device mode info.  
    // Event type info, is encoded into iCombos[i][1].
    
    TInt possibleMatch(-1);  //  iCombos index that might match
    for (i = 0; i < iCombos->Count(); i++) 
        {
        if ( (*(*iCombos)[i])[2] == aComboFirstCodeCandidate) 
            {
            //Check device mode and event type 
    		TUint16 comboMode = ((*(*iCombos)[i])[0] GET_MODS) >> 16; 
    		TUint16 comboType = ((*(*iCombos)[i])[1]);
    		
    		if((comboType & KKefIsDelayedOnCombo)) //We've found a special combo
    		    {
    		        __KEFLOGSTRING("CKefMapImpl::GetFirstKey: IS a special event");
    		        if((iCurrentDeviceMode != comboMode) && (comboMode != KKefModeDefault))
    		            {
    		                //but it is not for correct devicemode
    		                 __KEFLOGSTRING("CKefMapImpl::GetFirstKey Wrong devicemode");
    		            }
    		        else
    		            {
    		               possibleMatch = i;
                            __KEFLOGSTRING1("CKefMapImpl::GetFirstKey SPECIAL ComboCandidate might hit iCombos[%d]",i);
        	               break;  
    		            }
    		    }
                        
            }                    
        }
	if ( possibleMatch >= 0 ) //We have apossible match
	{
	    
		mapValue.iKeyCode = (*(*iCombos)[possibleMatch])[0] STRIP_MODS;
        mapValue.iEventType = (*(*iCombos)[possibleMatch])[1]; 

	}
	return mapValue;
  }
//END NEW CODE  
// End of File
