diff -r 000000000000 -r d54f32e146dd tactilefeedback/tactilefeedbackclient/src/touchfeedbackimpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tactilefeedback/tactilefeedbackclient/src/touchfeedbackimpl.cpp Thu Dec 17 08:53:38 2009 +0200 @@ -0,0 +1,1774 @@ +/* +* Copyright (c) 2007-2009 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: This is the actual Client API implementation. +* Part of: Tactile Feedback. +* +*/ + +#include +#include +#include +#include +#include +#include + +#include "touchfeedbackimpl.h" +#include "touchfeedbackclient.h" +#include "touchfeedbackregistry.h" +#include "touchfeedbackclientpanic.h" + +/** + * This is the maximum number that can be used as feedback area identifier. + * after this the values starting from 1 are taken into use again when + * new areas are added. This also defines the maximum number of feedback + * areas that one application can have. + */ +const TInt KTouchMaxFeedbackAreaIdentifier = 5000; + + +// ======== LOCAL FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// Cleanup items for removing latest item from the given array. These are used +// for cleanup in case something leaves when we are adding new area +// to the registry. +// --------------------------------------------------------------------------- +// +void CleanupCacheArray( TAny* aPtr ) + { + RArray* array = + static_cast*> ( aPtr ); + + TInt index = array->Count() - 1; + + // If we are removing control from cache, then we have to clean the + // associated area array also, or otherwise there will be a memory leak. + (*array)[index].iAreaArray.Reset(); + (*array)[index].iAreaArray.Close(); + + array->Remove( array->Count()-1 ); + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CleanupAreaArray( TAny* aPtr ) + { + RArray* array = + static_cast*> ( aPtr ); + + array->Remove( array->Count()-1 ); + } + + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// Constructor. +// --------------------------------------------------------------------------- +// +CTouchFeedbackImpl::CTouchFeedbackImpl() : + iAudioEnabledForThisApp( ETrue ), + iVibraEnabledForThisApp( ETrue ) + { + } + +// --------------------------------------------------------------------------- +// We check first if Tactile Feedback feature is supported in this device +// and only construct the iClient -member in case needed. We thus use +// the existence of iClient as feature check in other API functions. +// +// This also means that iClient member needs to be checked in all the API +// functions, so that we won't cause a KERN-EXEC 3 by trying to access +// a NULL pointer in case the feature is disabled. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::ConstructL() + { + TRACE("CTouchFeedbackImpl::ConstructL - Begin"); + + // Check if the whole Tactile Feedback feature is supported or not + + // Initialize feature manager, just in case we happen to be the first + // one using it in this thread. + FeatureManager::InitializeLibL(); + + TBool featureSupported = + FeatureManager::FeatureSupported( KFeatureIdTactileFeedback ); + + // We don't need feature manager anymore, as it is not possible + // to change this feature on/off at run-time + FeatureManager::UnInitializeLib(); + + // Only create client in case feature is supported + if ( featureSupported ) + { + iClient = CTouchFeedbackClient::NewL( *this ); + } + else + { + TRACE("Tactile Feedback feature is not supported"); + } + + // Check if pen usage is enabled. We use this information later for + // disabling area registry -based feedback in case pen usage is not + // enabled. + iPenEnabled = AknLayoutUtils::PenEnabled(); + + TRACE3("CTouchFeedbackImpl::ConstructL - End, feature supported = %d, pen Enabled: %d", featureSupported, iPenEnabled); + } + +// --------------------------------------------------------------------------- +// We cannot afford to leave here (at least for the moment), because +// in that case the whole system won't boot up. Constructing of the +// client (in ConstructL) will fail if click plugin is not configured in +// WSINI.INI file, and that can happen very easily at least in the beginning +// when this is a new feature. Thus we TRAP any leaves here. +// +// This also means that iClient member needs to be checked in all the API +// functions. We will also use this for recognizing the situation when +// touch feedback is not supported (in that case we won't even try to +// instantiate the client). +// --------------------------------------------------------------------------- +// +CTouchFeedbackImpl* CTouchFeedbackImpl::New() + { + CTouchFeedbackImpl* self = new CTouchFeedbackImpl; + if ( self ) + { + TRAP_IGNORE( self->ConstructL() ); + } + return self; + } + +// --------------------------------------------------------------------------- +// Destructor. +// --------------------------------------------------------------------------- +// +CTouchFeedbackImpl::~CTouchFeedbackImpl() + { + TRACE("CTouchFeedbackImpl::~CTouchFeedbackImpl"); + + delete iClient; + + TInt i = 0; + + for ( i=0; i < iRegistryArray.Count(); i++ ) + { + delete iRegistryArray[i]; + } + + for ( i=iControlCache.Count()-1; i >= 0; i-- ) + { + RemoveControlFromCache( i ); + } + + iRegistryArray.Close(); + iControlCache.Close(); + } + +// --------------------------------------------------------------------------- +// We return ETrue if iClient exist, because instantiation of the client is +// not done in case the feature is not enabled. And on the other hand if +// instantiation of client fails, then using of feedback is not possible +// anyway. +// --------------------------------------------------------------------------- +// +TBool CTouchFeedbackImpl::TouchFeedbackSupported() + { + if ( iClient ) + { + return ETrue; + } + else + { + return EFalse; + } + } + +// --------------------------------------------------------------------------- +// Store the current status of feedback for this application, and +// notify server of the change. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::SetFeedbackEnabledForThisApp( TBool aEnabled ) + { + TRACE2( "CTouchFeedbackImpl::SetFeedbackEnabledForThisApp %d", aEnabled ); + + // Only notify server when enabled value really changes. + if ( iClient && + (( aEnabled != iAudioEnabledForThisApp ) || + (aEnabled != iVibraEnabledForThisApp ) )) + { + iClient->RegistryChanged(); + } + + iAudioEnabledForThisApp = aEnabled; + iVibraEnabledForThisApp = aEnabled; + } + +// --------------------------------------------------------------------------- +// Here we just return the current status. +// --------------------------------------------------------------------------- +// +TBool CTouchFeedbackImpl::FeedbackEnabledForThisApp() + { + return iAudioEnabledForThisApp || iVibraEnabledForThisApp; + } + +// --------------------------------------------------------------------------- +// This functions main purpose is to do all possible parameter checks to +// the arguments before actually setting the area. +// +// #1 Check that feedback is enabled +// #2 Check that we weren't given a NULL pointer as control. +// #3 Check that feedback type and pointer event are supported +// #4 Check that we can generate a client handle from the control +// (if not, then there is something wrong with given control) +// #5 Call overridden method SetFeedbackArea for actually setting the +// feedback area. +// --------------------------------------------------------------------------- +// +TInt CTouchFeedbackImpl::SetFeedbackArea( + const CCoeControl* aControl, + TUint32 aIndex, + TRect aRect, + TTouchLogicalFeedback aFeedbackType, + TTouchEventType aEventType ) + { + TRACE("CTouchFeedbackImpl::SetFeedbackArea - Begin"); + + // #1 + if ( !iClient ) + { + return KErrNone; + } + + // #2 + if ( !aControl ) + { + TRACE("CTouchFeedbackImpl::SetFeedbackArea - Err: NULL Control"); + return KErrArgument; + } + + // #3 + if ( aFeedbackType > ETouchFeedbackSensitive || + ( aEventType!= ETouchEventStylusDown ) ) + { + TRACE("CTouchFeedbackImpl::SetFeedbackArea - Err: Unsupported params"); + return KErrNotSupported; + } + + // #4 + TUint32 clientHandle = ClientHandle( aControl ); + + if ( !clientHandle ) + { + TRACE("CTouchFeedbackImpl::SetFeedbackArea - Err: Invalid Control"); + return KErrArgument; + } + + // #5 + TInt err(KErrGeneral); + CFeedbackSpec* spec = CFeedbackSpec::New(); + if (spec) + { + spec->AddFeedback(aEventType, aFeedbackType); + err = SetFeedbackArea(aControl, aIndex, aRect, spec); + delete spec; + spec = NULL; + } + + TRACE2("CTouchFeedbackImpl::SetFeedbackArea - End, err = %d", err ); + return err; + } + +// --------------------------------------------------------------------------- +// Here we remove the given area from registry. +// +// We still keep the control in the registry even if would have no areas, +// because otherwise we'll lose feedback disabled/enabled information. +// +// #1 First check that given area is actually found +// #2 Find corresponsing registry entry +// #3 Remove feedback area entry from control cache +// #4 Finally remove the actual area from registry, and notify server +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::RemoveFeedbackArea( + const CCoeControl* aControl, + TUint32 aIndex ) + { + TRACE3( "CTouchFeedbackImpl::RemoveFeedbackArea: 0x%x, %d", aControl, aIndex ); + + if ( iClient ) + { + TInt cacheIndex = KErrNotFound; + TInt areaIndex = KErrNotFound; + + FindAreaFromCache( aControl, aIndex, cacheIndex, areaIndex ); + + // #1 Only do something in case the area was found + if ( cacheIndex >=0 && areaIndex >= 0 ) + { + // #2 + TInt registryIndex = FindWindowFromRegistry ( + iControlCache[cacheIndex].iClientHandle ); + + TInt areaId = + iControlCache[cacheIndex].iAreaArray[areaIndex].iAreaId; + + // #3 Remove this feedback area entry + iControlCache[cacheIndex].iAreaArray.Remove( areaIndex ); + + iNbOfFeedbackAreas--; + + // #4 Remove the area from registry + if ( registryIndex != KErrNotFound ) + { + iRegistryArray[registryIndex]->RemoveFeedbackArea( areaId ); + + iClient->RegistryChanged(); + } + } + } + } + +// --------------------------------------------------------------------------- +// Here we return all areas related to given control. +// +// #1 Only do something in case the control is found from cache +// #2 Find corresponding registry index. +// #3 Remove all feedback areas from the registry. +// #4 Remove the control from cache +// #5 Notify server, if some areas were really removed. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::RemoveFeedbackForControl( + const CCoeControl* aControl ) + { + TRACE2("CTouchFeedbackImpl::RemoveFeedbackForControl: 0x%x", aControl); + + if ( iClient ) + { + TInt controlIndex = FindControlFromCache( aControl ); + + // #1 + if ( controlIndex >= 0 ) // If control was found + { + TBool changesMade = EFalse; + + TControlCacheEntry& entry( iControlCache[controlIndex] ); + + // #2 + TInt registryIndex = + FindWindowFromRegistry( entry.iClientHandle ); + + // #3 If registry entry was found, then remove all feedback + // areas. + if ( registryIndex >= 0) + { + for ( TInt i=0; i < entry.iAreaArray.Count(); i++ ) + { + iRegistryArray[registryIndex]-> + RemoveFeedbackArea( + entry.iAreaArray[i].iAreaId ); + + changesMade = ETrue; + + iNbOfFeedbackAreas--; + } + } + + // #4 Anyway remove controls feedback areas from cache + RemoveControlFromCache( controlIndex ); + + // #5 Notify server of changes + if ( changesMade ) + { + iClient->RegistryChanged(); + } + } + } + } + +// --------------------------------------------------------------------------- +// Here we change the rectangle of existing feedback area. +// +// #1 Only do something in case the area is really found. +// #2 Find corresponding registry entry +// #3 If the entry was found, then change the feedback area +// #4 Only notify the server when the area really changed +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::ChangeFeedbackArea( const CCoeControl* aControl, + TUint32 aIndex, + TRect aNewRect ) + { + TRACE( "CTouchFeedbackImpl::ChangeFeedbackArea: 0x%x" ); + + if ( iClient ) + { + TInt cacheIndex = KErrNotFound; + TInt areaIndex = KErrNotFound; + + FindAreaFromCache( aControl, aIndex, cacheIndex, areaIndex ); + + // #1 + if ( cacheIndex >= 0 && areaIndex >= 0 ) + { + TInt id = + iControlCache[cacheIndex].iAreaArray[areaIndex].iAreaId; + + // #2 + TInt registryIndex = + FindWindowFromRegistry ( + iControlCache[cacheIndex].iClientHandle ); + + // #3 + if ( registryIndex != KErrNotFound ) + { + TBool changed = + iRegistryArray[registryIndex]->ChangeFeedbackArea( + id, + aNewRect ); + + // #4 Only send upates to server when something + // really changed. + if ( changed ) + { + iClient->RegistryChanged(); + } + } + } + } + } + +// --------------------------------------------------------------------------- +// Here we change the feedback type of existing feedback area. +// +// #1 Only do something in case the area is really found. +// #2 Find corresponding registry entry +// #3 If the entry was found, then change the feedback area +// #4 Only notify the server when the feedback type really changed +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::ChangeFeedbackType( const CCoeControl* aControl, + TUint32 aIndex, + TTouchLogicalFeedback aNewType ) + { + TRACE4("CTouchFeedbackImpl::ChangeFeedbackType: 0x%x, %d, %d", aControl, aIndex, aNewType); + + if ( iClient ) + { + TInt cacheIndex = KErrNotFound; + TInt areaIndex = KErrNotFound; + + FindAreaFromCache( aControl, aIndex, cacheIndex, areaIndex ); + + // #1 + if ( cacheIndex >= 0 && areaIndex >= 0 ) + { + TInt id = iControlCache[cacheIndex].iAreaArray[areaIndex].iAreaId; + + // #2 + TInt registryIndex = + FindWindowFromRegistry ( + iControlCache[cacheIndex].iClientHandle ); + + // #3 + if ( registryIndex != KErrNotFound ) + { + TBool changed = + iRegistryArray[registryIndex]->ChangeFeedbackType( + id, + aNewType, + // Feedback type change for up event is not supported + // by this function. + static_cast( 0xFFFFFFFF ) ); + + // #4 Only send upates to server when something + // really changed. + if ( changed ) + { + iClient->RegistryChanged(); + } + } + } + } + } + +// --------------------------------------------------------------------------- +// Here we change move the given feedback area to first priority, so that +// it will be the topmost in its window. +// +// #1 Only do something in case the area is really found. +// #2 Find corresponding registry entry +// #3 If the entry was found, then change the feedback area +// #4 Only notify the server when the move was successfull +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::MoveFeedbackAreaToFirstPriority( + const CCoeControl* aControl, + TUint32 aIndex ) + { + if ( iClient ) + { + TInt cacheIndex = KErrNotFound; + TInt areaIndex = KErrNotFound; + + FindAreaFromCache( aControl, aIndex, cacheIndex, areaIndex ); + + // #1 + if ( cacheIndex >= 0 && areaIndex >= 0 ) + { + TInt id = iControlCache[cacheIndex].iAreaArray[areaIndex].iAreaId; + + // #2 + TInt registryIndex = + FindWindowFromRegistry ( + iControlCache[cacheIndex].iClientHandle ); + + // #3 + if ( registryIndex != KErrNotFound ) + { + TInt err = + iRegistryArray[registryIndex]-> + MoveFeedbackAreaToFirstPriority( id ); + + // #4 + if ( err == KErrNone ) + { + TRACE3("CTouchFeedbackImpl::MoveFeedbackAreaToFirstPriority 0x%x, %d", aControl, aIndex); + iClient->RegistryChanged(); + } + } + } + } + } + +// --------------------------------------------------------------------------- +// Pass request to server in case feedback is enabled for this +// application. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::InstantFeedback( TTouchLogicalFeedback aType ) + { + if ( iClient && ( iAudioEnabledForThisApp || iVibraEnabledForThisApp ) ) + { + TRACE2("CTouchFeedbackImpl::InstantFeedback %d", aType); + iClient->ImmediateFeedback( + aType, + iVibraEnabledForThisApp, + iAudioEnabledForThisApp ); + } + } + +// --------------------------------------------------------------------------- +// In this overload we only play direct feedback if the feedback has +// not been disabled for this control. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::InstantFeedback( + const CCoeControl* aControl, + TTouchLogicalFeedback aType ) + { + if ( iClient && ( iAudioEnabledForThisApp || iVibraEnabledForThisApp ) ) + { + + // First take application level vibra and audio enablers + // as default + TBool vibraEnabled = iVibraEnabledForThisApp; + TBool audioEnabled = iAudioEnabledForThisApp; + + TInt cacheIndex = FindControlFromCache( aControl ); + + + // Check control level disablers for vibra and audio. + // (These can only prevent vibra or audio effect playback, + // but not to force it on) + if ( cacheIndex >=0 && vibraEnabled ) + { + vibraEnabled = !iControlCache[cacheIndex].iVibraDisabled; + } + + if ( cacheIndex >=0 && audioEnabled ) + { + audioEnabled = !iControlCache[cacheIndex].iAudioDisabled; + } + + TRACE3("CTouchFeedbackImpl::InstantFeedback 0x%x, %d", aControl, aType); + iClient->ImmediateFeedback( aType, vibraEnabled, audioEnabled ); + } + } + +// --------------------------------------------------------------------------- +// It is not enough to check that the control exists in the cache, we +// also need to check that it actually has some areas registered. +// --------------------------------------------------------------------------- +// +TBool CTouchFeedbackImpl::ControlHasFeedback( const CCoeControl* aControl ) + { + TBool hasFeedback = EFalse; + + if ( iClient ) + { + for ( TInt i=0; i < iControlCache.Count(); i++ ) + { + if ( iControlCache[i].iControl == aControl ) + { + if ( iControlCache[i].iAreaArray.Count() > 0 ) + { + hasFeedback = ETrue; + } + + // Break out from loop anyway, since we found the + // control + break; + } + } + } + + return hasFeedback; + } + +// --------------------------------------------------------------------------- +// Control has feedback if it has a feedback identifier with given index +// number. +// --------------------------------------------------------------------------- +// +TBool CTouchFeedbackImpl::ControlHasFeedback( + const CCoeControl* aControl, + TUint32 aIndex ) + { + TBool hasArea = EFalse; + + if ( iClient ) + { + TInt cacheIndex = KErrNotFound; + TInt areaIndex = KErrNotFound; + + FindAreaFromCache( aControl, aIndex, cacheIndex, areaIndex ); + + if ( cacheIndex >= 0 && areaIndex >= 0 ) + { + hasArea = ETrue; + } + } + + return hasArea; + } + +// --------------------------------------------------------------------------- +// This overload is only wrapper to the real function +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::EnableFeedbackForControl( + const CCoeControl* aControl, + TBool aEnable ) + { + EnableFeedbackForControl( aControl, aEnable, aEnable ); + } + +// --------------------------------------------------------------------------- +// #1 Find control from the cache +// #2 In case control was found, disable/enable it's registry entries, and +// also take into accound it's dimming status +// #3 If control was not found, then add it to cache +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::EnableFeedbackForControl( + const CCoeControl* aControl, + TBool aEnableVibra, + TBool aEnableAudio ) + { + TRACE4( "CTouchFeedbackImpl::EnableFeedbackForControl - 0x%x Vibra:%d Audio:=%d", + aControl, aEnableVibra, aEnableAudio ); + + if ( aControl ) + { + // #1 + TInt cacheIndex = FindControlFromCache( aControl ); + + if ( cacheIndex >= 0 ) + { + // #2 + TControlCacheEntry& entry ( iControlCache[cacheIndex] ); + + entry.iVibraDisabled = !aEnableVibra; + entry.iAudioDisabled = !aEnableAudio; + + // Let's update visibility and dimming information also now + // that we have the pointer to control (due to broken object + // provider chain we might not have the correct information on + // the moment). + entry.iDimmed = aControl->IsDimmed(); + entry.iVisible = aControl->IsVisible(); + + if ( ControlsAreasActive( cacheIndex ) ) + { + // If control is active (undimmed and visible), then we + // set the areas according to parameters. + DoEnableControlsAreasInRegistry( cacheIndex, + aEnableVibra, + aEnableAudio, + aControl->IsVisible() ); + } + else + { + // If control is not active, then we disable all feedback. + DoEnableControlsAreasInRegistry( cacheIndex, + EFalse, + EFalse, + aControl->IsVisible() ); + } + } + else if ( !aEnableVibra || !aEnableAudio ) + { + // #3 We have to add control to the cache in case + // it is not there yet (and disabling is wanted), because + // otherwise it will be impossible to block feedback + TUint32 clientHandle = ClientHandle( aControl ); + + // We need to rely elsewhere that client handle exist if control + // is found in the cache --> Only make new entry if client + // handle can be aquired. + if ( clientHandle > 0 ) + { + TControlCacheEntry newCacheEntry; + + newCacheEntry.iControl = aControl; + newCacheEntry.iClientHandle = clientHandle; + newCacheEntry.iVibraDisabled = !aEnableVibra; + newCacheEntry.iAudioDisabled = !aEnableAudio; + newCacheEntry.iVisible = aControl->IsVisible(); + newCacheEntry.iDimmed = aControl->IsDimmed(); + + iControlCache.Append( newCacheEntry ); + } + } + } + } + +// --------------------------------------------------------------------------- +// Store the current status of audio and vibra feedbacks for this +// application, and notify server of the change. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::SetFeedbackEnabledForThisApp( + TBool aVibraEnabled, + TBool aAudioEnabled ) + { + TRACE3( "CTouchFeedbackImpl::SetFeedbackEnabledForThisApp %d %d", aVibraEnabled, aAudioEnabled ); + + // Only notify server when enabled value really changes. + if ( iClient && + (( aAudioEnabled != iAudioEnabledForThisApp ) || + (aVibraEnabled != iVibraEnabledForThisApp ) )) + { + iClient->RegistryChanged(); + } + + iAudioEnabledForThisApp = aAudioEnabled; + iVibraEnabledForThisApp = aVibraEnabled; + } + +// --------------------------------------------------------------------------- +// Simple utility for CTouchFeedbackClient +// --------------------------------------------------------------------------- +// +RPointerArray* CTouchFeedbackImpl::RegistryArray() + { + return &iRegistryArray; + } + +// --------------------------------------------------------------------------- +// Simple utility for CTouchFeedbackClient +// +// We return zero in case pen is not supported in current layout. This way +// area registry is disabled in non-touch powered layouts, but feedback areas +// are still kept in memory so that feedback can be enabled again in case +// layout changes. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::GetAreaCount( TInt& aAreaCount, TInt& aWindowCount ) + { + aWindowCount = 0; + aAreaCount = 0; + + if ( iPenEnabled ) + { + aWindowCount = iRegistryArray.Count(); + aAreaCount = iNbOfFeedbackAreas; + } + } + +// --------------------------------------------------------------------------- +// This function is called by the application framework when any control's +// visibility or dimming status has changed. +// +// We need to determine here whether the given control has any feedback, +// and whether its current state requires a change in feedback +// areas (for e.g. feedback areas will be disabled for any dimmed control). +// +// #1 Find control from cache (we don't need to do anything in case it +// is not found. +// #2 Check if control's feedback areas are now active (based on dimming, +// visibility and feedback disabling). Also check control's current +// visibility status as itself, as this determines whether the area will +// be put to registry at all. +// #3 Save control's current dimming and visibility status +// #4 Check again if control's areas should be active +// #5 Disable/Enable areas in case needed +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::ControlVisibilityChanged( + const CCoeControl* aControl ) + { + if ( iClient && aControl ) + { + // #1 + TInt cacheIndex = FindControlFromCache( aControl ); + + if ( cacheIndex >= 0 ) + { + // #2 + TBool oldStatus = ControlsAreasActive( cacheIndex ); + TBool oldVisible = iControlCache[cacheIndex].iVisible; + + // #3 + iControlCache[cacheIndex].iVisible = aControl->IsVisible(); + iControlCache[cacheIndex].iDimmed = aControl->IsDimmed(); + + + // #4 + TBool newStatus = ControlsAreasActive( cacheIndex ); + + // #5 + if ( ( oldStatus != newStatus ) || + ( oldVisible != aControl->IsVisible() ) ) + { + if ( newStatus ) // If control is now active + { + DoEnableControlsAreasInRegistry( + cacheIndex, + !iControlCache[cacheIndex].iVibraDisabled, + !iControlCache[cacheIndex].iAudioDisabled, + aControl->IsVisible() ); + } + else + { + DoEnableControlsAreasInRegistry( cacheIndex, + EFalse, + EFalse, + aControl->IsVisible() ); + } + } + } + } + } + +// --------------------------------------------------------------------------- +// Here we check whether the pen support status has changed, and update +// registry in case needed. +// +// Notice that this has not effect on direct feedback: It is completely on +// UI control's responsibility to check if pen is supported with direct +// feedback (direct feedback is not automatically disabled because that +// would cause problems in compatibility mode). +// +// Also notice that we don't actually destroy any feedback areas when pen +// usage is not supported: We just report zero areas to server side, but +// still keep everything in memory (see GetAreaCount -function) +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::LayoutChanged() + { + if ( iClient ) + { + TBool penEnabledNow = AknLayoutUtils::PenEnabled(); + + TRACE2("CTouchFeedbackImpl::LayoutChanged, PenEnabled = %d", penEnabledNow); + + if ( penEnabledNow != iPenEnabled ) + { + iPenEnabled = penEnabledNow; + + iClient->RegistryChanged(); + } + } + } + +// new functions from MTouchFeedback that came in 5.2 +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TBool CTouchFeedbackImpl::FeedbackEnabledForThisApp( TTouchFeedbackType aFeedbackType ) + { + TBool feedbackEnabled( ETrue ); + + if ( (aFeedbackType & ETouchFeedbackAudio) && !iAudioEnabledForThisApp ) + { + feedbackEnabled = EFalse; + } + + if ( (aFeedbackType & ETouchFeedbackVibra) && !iVibraEnabledForThisApp ) + { + feedbackEnabled = EFalse; + } + + if ( !aFeedbackType ) + { + feedbackEnabled = EFalse; + } + + return feedbackEnabled; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::StartFeedback( const CCoeControl* aControl, + TTouchContinuousFeedback aType, + const TPointerEvent* /*aPointerEvent*/, + TInt aIntensity, + TTimeIntervalMicroSeconds32 aTimeout ) + { + if ( iClient && ( iAudioEnabledForThisApp || iVibraEnabledForThisApp ) ) + { + // First take application level vibra and audio enablers + // as default + TBool vibraEnabled = iVibraEnabledForThisApp; + TBool audioEnabled = iAudioEnabledForThisApp; + + TInt cacheIndex = FindControlFromCache( aControl ); + + + // Check control level disablers for vibra and audio. + // (These can only prevent vibra or audio effect playback, + // but not to force it on) + if ( cacheIndex >=0 && vibraEnabled ) + { + vibraEnabled = !iControlCache[cacheIndex].iVibraDisabled; + } + + if ( cacheIndex >=0 && audioEnabled ) + { + audioEnabled = !iControlCache[cacheIndex].iAudioDisabled; + } + + if ( vibraEnabled || audioEnabled ) + { + TRACE4("CTouchFeedbackImpl::StartFeedback, type:=%d intensity:%d, timeout:%d", aType, aIntensity, aTimeout); + TUint32 clientHandle = ClientHandle( aControl ); + iClient->StartFeedback( clientHandle, + aType, + aIntensity, + aTimeout ); + } + } + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::ModifyFeedback( const CCoeControl* aControl, + TInt aIntensity ) + { + if ( iClient ) + { + TRACE2("CTouchFeedbackImpl::ModifyFeedbac, intensity:%d, timeout:%d", aIntensity); + TUint32 clientHandle = ClientHandle( aControl ); + iClient->ModifyFeedback( clientHandle, aIntensity ); + } + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::StopFeedback( const CCoeControl* aControl ) + { + if ( iClient ) + { + TUint32 clientHandle = ClientHandle( aControl ); + iClient->StopFeedback( clientHandle ); + } + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TInt CTouchFeedbackImpl::SetFeedbackEnabledForDevice( TTouchFeedbackType aFeedbackType ) + { + TInt ret( KErrGeneral ); + if ( iClient ) + { + ret = iClient->SetFeedbackEnabledForDevice( aFeedbackType ); + } + return ret; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TTouchFeedbackType CTouchFeedbackImpl::FeedbackEnabledForDevice() + { + TTouchFeedbackType enabled( static_cast(0) ); + if ( iClient ) + { + enabled = iClient->FeedbackEnabledForDevice(); + } + return enabled; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::InstantFeedback( const CCoeControl* aControl, + TTouchLogicalFeedback aType, + const TPointerEvent& /*aPointerEvent*/ ) + { + InstantFeedback(aControl, aType); + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +TInt CTouchFeedbackImpl::SetFeedbackArea( const CCoeControl* aControl, + TUint32 aIndex, + TRect aRect, + CFeedbackSpec* aFeedbackSpec ) + { + // #1 + if ( !iClient ) + { + return KErrNone; + } + + // #2 + if ( !aControl ) + { + TRACE("CTouchFeedbackImpl::SetFeedbackArea(spec) - Err: NULL Control"); + return KErrArgument; + } + + // #4 + TUint32 clientHandle = ClientHandle( aControl ); + + if ( !clientHandle ) + { + TRACE("CTouchFeedbackImpl::SetFeedbackArea(spec) - Err: Invalid Control"); + return KErrArgument; + } + + // #5 + TRAPD( err, + DoSetFeedbackAreaL( + aControl, + aIndex, + aRect, + aFeedbackSpec, + clientHandle ) ); + + if (err) + { + TRACE2("CTouchFeedbackImpl::SetFeedbackArea(spec) err = %d", err ); + } + + return err; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::InstantFeedback( const CCoeControl* aControl, + TTouchLogicalFeedback aType, + TTouchFeedbackType aFeedbackType, + const TPointerEvent& /*aPointerEvent*/ ) + { + if ( iClient && ( iAudioEnabledForThisApp || iVibraEnabledForThisApp ) ) + { + // Initialize vibra and audio enablers as given in param. + TBool vibraEnabled = aFeedbackType & ETouchFeedbackVibra; + TBool audioEnabled = aFeedbackType & ETouchFeedbackAudio; + + // Check application level vibra and audio enablers + if ( !iVibraEnabledForThisApp ) + { + vibraEnabled = EFalse; + } + if ( !iAudioEnabledForThisApp ) + { + audioEnabled = EFalse; + } + + TInt cacheIndex = FindControlFromCache( aControl ); + + + // Check control level disablers for vibra and audio. + // (These can only prevent vibra or audio effect playback, + // but not to force it on) + if ( cacheIndex >=0 && vibraEnabled ) + { + vibraEnabled = !iControlCache[cacheIndex].iVibraDisabled; + } + + if ( cacheIndex >=0 && audioEnabled ) + { + audioEnabled = !iControlCache[cacheIndex].iAudioDisabled; + } + + TRACE5("CTouchFeedbackImpl::InstantFeedback, 0x%x, type:=%d Vibra:%d Audio:%d", + aControl, aType, vibraEnabled, audioEnabled); + iClient->ImmediateFeedback( aType, vibraEnabled, audioEnabled ); + } + } + +// --------------------------------------------------------------------------- +// Here we do the actual work for adding new area to the registry +// (or updating an existing one). +// +// #1 Check that control exist in the cache +// #2a If it does not exist, then add it +// #2b If control exists, but area not, then add new area +// #2c If control and area both exist, then use existing id +// #3 Find corresponding registry entry +// #4 Create new entry if this was the first area for this window +// #5 Determine if new area should actually be active, and then +// add it to registry +// #6 Inform server if we added an active area +// +// We use specific cleanup items for removing the added area and/or control +// from the control cache in caes adding of the area to the actual +// registry fails. This way actual registry and control cache will remain +// in sync no matter what happens. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::DoSetFeedbackAreaL( + const CCoeControl* aControl, + TUint32 aIndex, + TRect aRect, + CFeedbackSpec* aFeedbackSpec, + TUint32 aClientHandle ) + { + TInt id = KErrNotFound; + TInt nbOfCleanupItems = 0; + TBool newArea = EFalse; + + // #1 + TInt cacheIndex = KErrNotFound; + TInt areaIndex = KErrNotFound; + FindAreaFromCache( aControl, aIndex, cacheIndex, areaIndex ); + + // #2a + if ( cacheIndex < 0 ) + { + id = GenerateUniqueIdL(); + + TControlCacheEntry newCacheEntry; + + newCacheEntry.iControl = aControl; + newCacheEntry.iClientHandle = aClientHandle; + newCacheEntry.iVibraDisabled = EFalse; + newCacheEntry.iAudioDisabled = EFalse; + newCacheEntry.iVisible = aControl->IsVisible(); + newCacheEntry.iDimmed = aControl->IsDimmed(); + + // New entry will be added at the end of array + // This assignment will also make it possible to use + // "cacheIndex" -variable in the rest of the function + // in all cases. + cacheIndex = iControlCache.Count(); + + iControlCache.AppendL( newCacheEntry ); + + // Use cleanup item to remove the control in case something + // fails later in this function. + CleanupStack::PushL( + TCleanupItem( CleanupCacheArray, &iControlCache ) ); + nbOfCleanupItems++; + + TAreaIndexEntry newAreaEntry; + + newAreaEntry.iIndex = aIndex; + newAreaEntry.iAreaId = id; + + iControlCache[cacheIndex].iAreaArray.AppendL( newAreaEntry ); + + // Use cleanup item to remove the area id in case something + // fails later in this function. + CleanupStack::PushL( + TCleanupItem( CleanupAreaArray, + &iControlCache[cacheIndex].iAreaArray) ); + nbOfCleanupItems++; + + newArea = ETrue; + + // Enable control state change reports on the new control + CCoeControl* tmp = const_cast( aControl ); + tmp->EnableReportControlStateChange( ETrue ); + } + // #2b + else if ( areaIndex < 0 ) + { + id = GenerateUniqueIdL(); + + TAreaIndexEntry newAreaEntry; + + newAreaEntry.iIndex = aIndex; + newAreaEntry.iAreaId = id; + + iControlCache[cacheIndex].iAreaArray.AppendL( newAreaEntry ); + + CleanupStack::PushL( + TCleanupItem( CleanupAreaArray, + &iControlCache[cacheIndex].iAreaArray) ); + nbOfCleanupItems++; + + newArea = ETrue; + } + // #2c + else + { + id = iControlCache[cacheIndex].iAreaArray[areaIndex].iAreaId; + } + + // #3 + TInt registryIndex = FindWindowFromRegistry ( aClientHandle ); + + // #4 + if ( registryIndex == KErrNotFound ) + { + // There isn't yet a registry entry for this window + // --> Create it + CTouchFeedbackRegistry* newRegistry = + CTouchFeedbackRegistry::NewL( aClientHandle ); + + CleanupStack::PushL( newRegistry ); + + registryIndex = iRegistryArray.Count(); + + iRegistryArray.AppendL( newRegistry ); + + CleanupStack::Pop( newRegistry ); + } + + // #5 Determine what is the vibra and audio status for the new area + // (if control is invisible or dimmed then both will be disabled) + TBool vibraEnabled = EFalse; + TBool audioEnabled = EFalse; + + TBool areaActive = ControlsAreasActive( cacheIndex ); + + if ( areaActive ) + { + vibraEnabled = !iControlCache[cacheIndex].iVibraDisabled; + audioEnabled = !iControlCache[cacheIndex].iAudioDisabled; + } + + + RArray feedbackEntries; + CleanupClosePushL( feedbackEntries ); + TTouchEventType eventType(ETouchEventStylusDown); + TTouchLogicalFeedback feedbackTypeDown = ETouchFeedbackNone; + TTouchLogicalFeedback feedbackTypeUp = ETouchFeedbackNone; + TTouchFeedbackType feedbackDown = ETouchFeedbackVibra; + TTouchFeedbackType feedbackUp = ETouchFeedbackVibra; + aFeedbackSpec->GetFeedbackSpec(feedbackEntries); + TInt feedbackItems = feedbackEntries.Count(); + for (TInt i=0;i 0 ) + { + iRegistryArray[registryIndex]->AddFeedbackAreaL( + aRect, feedbackTypeDown, feedbackDown, feedbackTypeUp, feedbackUp, + eventType, id, vibraEnabled, audioEnabled, aControl->IsVisible() ); + } + CleanupStack::PopAndDestroy( &feedbackEntries ); + + // We can now remove the cleanup items, because nothing can fail + // anymore. + CleanupStack::Pop( nbOfCleanupItems ); + + // We can also update the counter now, when we know that the area + // was actually added + if ( newArea ) + { + iNbOfFeedbackAreas++; + } + + // #6 We only need to inform server in case we added an active area + // to the registry. + if ( areaActive ) + { + iClient->RegistryChanged(); + } + } + +// --------------------------------------------------------------------------- +// A simple for -loop should be enough here, as we are not likely to have +// that many windows in one application process. +// --------------------------------------------------------------------------- +// +TInt CTouchFeedbackImpl::FindWindowFromRegistry( TUint aWindowHandle ) + { + TInt index = KErrNotFound; + + for ( TInt i=0; i < iRegistryArray.Count(); i++ ) + { + if ( iRegistryArray[i]->WindowHandle() == aWindowHandle ) + { + index = i; + break; + } + } + + return index; + } + +// --------------------------------------------------------------------------- +// Just ask client to do updates immediately +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::FlushRegistryUpdates( ) + { + if ( iClient ) + { + TRACE("CTouchFeedbackImpl::FlushRegistryUpdates" ); + iClient->FlushUpdates(); + } + } + +// --------------------------------------------------------------------------- +// We use integer numers starting from 1 as area identifiers. +// +// There is a bookkeeping of last used identifier, and we'll start from one +// again after reaching the maximum value. +// +// There is also a separate bookkeeping to know if our id values have been +// rotated at least once or not. From that we know if we have to check that +// the id is not in use already (if we are on the first round, then there +// cannot be any area with the new id). +// +// Notice that it should be checked already _before_ calling this function, +// whether it is actually possible to find an unused identifier anymore. +// --------------------------------------------------------------------------- +// +TUint CTouchFeedbackImpl::GenerateUniqueIdL() + { + // First check that there actually is an identifier that we can find + // (i.e. check that maximum number of areas has not been reached) + if ( iNbOfFeedbackAreas >= KTouchMaxFeedbackAreaIdentifier ) + { + User::Leave( KErrNoMemory ); + } + + iCurrentId++; + + if ( iCurrentId > KTouchMaxFeedbackAreaIdentifier ) + { + iCurrentId = 1; + + iCurrentIdCounterRotated = ETrue; + } + + if ( iCurrentIdCounterRotated ) + { + while ( FeedbackAreaExists( iCurrentId ) ) + { + iCurrentId++; + + if ( iCurrentId > KTouchMaxFeedbackAreaIdentifier ) + { + iCurrentId = 1; + } + } + + } + + return iCurrentId; + } + +// --------------------------------------------------------------------------- +// Scan through local registry in order to know whether the area with +// given identifier exists. +// +// This function is only used when creating a new unique identifier. +// --------------------------------------------------------------------------- +// +TBool CTouchFeedbackImpl::FeedbackAreaExists( TUint aAreaId ) + { + TBool areaExists = EFalse; + + for ( TInt i=0; i < iRegistryArray.Count() && !areaExists; i++ ) + { + RArray* entryArray = + iRegistryArray[i]->WindowRegistry(); + + for ( TInt j=0; j < entryArray->Count() && !areaExists; j++ ) + { + if ( (*entryArray)[j].iId == aAreaId ) + { + areaExists = ETrue; + } + } + } + + return areaExists; + } + +// --------------------------------------------------------------------------- +// Because we use the so called "client handle" for identifying windows on +// client- and server sides, we must be able to generate a client handle +// from CCoeControl -pointer. +// +// The algorithm: +// +// #1 Loop as long as we haven't reached the top-level control, and we still +// haven't found the first window-owning control. +// +// #2 If this is a window-owning control, then it's address is the +// client-side handle +// +// #3 Otherwise move up to next parent +// +// #4 If this control doesn't have paren't pointer, then try to get +// a pointer to this control's window. +// +// #5 If we got the window, then use the ClientHandle -function for +// retrieving the pointer from server side +// +// #6 If we don't have parent- or window pointers, then give up. +// --------------------------------------------------------------------------- +// +TUint32 CTouchFeedbackImpl::ClientHandle( const CCoeControl* aControl ) + { + TUint32 clientHandle = 0; + + const CCoeControl* parent = aControl; + + // #1 + while ( clientHandle == 0 && parent ) + { + if ( parent->OwnsWindow() ) + { + // #2 + clientHandle = reinterpret_cast( parent ); + } + else + { + // #3 + parent = parent->Parent(); + + if ( !parent ) + { + // #4 + RDrawableWindow* window = aControl->DrawableWindow(); + + if ( window ) + { + // #5 + TRACE("CTouchFeedbackImpl::ClientHandle - Ask handle from wserv - begin" ); + clientHandle = window->ClientHandle(); + TRACE("CTouchFeedbackImpl::ClientHandle - Ask handle from wserv - end" ); + } + else + { + // #6 + TRACE("CTouchFeedbackImpl::ClientHandle - Error: No window defined, not possible to get handle!!"); + } + + } + } + } + + return clientHandle; + } + +// --------------------------------------------------------------------------- +// We could optimize this search in case we get very many controls to the +// array, but so far it hasn't been necessary. +// --------------------------------------------------------------------------- +// +TInt CTouchFeedbackImpl::FindControlFromCache( + const CCoeControl* aControl ) + { + TInt position = KErrNotFound; + + for ( TInt i=0; i < iControlCache.Count(); i++ ) + { + if ( iControlCache[i].iControl == aControl ) + { + position = i; + break; + } + } + + return position; + } + +// --------------------------------------------------------------------------- +// If this search is starting to take too long time, then the best +// optimization would be to optimize the FindControlFromCache -function, +// as there can potentially be very many controls, but not likely very many +// ares per each control. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::FindAreaFromCache( + const CCoeControl* aControl, + TUint32 aIndex, + TInt& aCacheIndex, + TInt& aAreaIndex ) + { + aAreaIndex = KErrNotFound; + + aCacheIndex = FindControlFromCache( aControl ); + + if ( aCacheIndex >= 0 ) + { + TControlCacheEntry& entry (iControlCache[aCacheIndex]); + + for ( TInt i=0; i < entry.iAreaArray.Count(); i++ ) + { + if ( entry.iAreaArray[i].iIndex == aIndex ) + { + aAreaIndex = i; + break; + } + } + } + } + +// --------------------------------------------------------------------------- +// Here we remove the control from the cache. It is assumed that +// corresponding feedback areas have been removed from registry +// already. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::RemoveControlFromCache( TInt aControlIndex ) + { + __ASSERT_ALWAYS( + aControlIndex >= 0 && aControlIndex < iControlCache.Count(), + Panic( ETouchClientPanicArrayAccess ) ); + + iControlCache[aControlIndex].iAreaArray.Reset(); + iControlCache[aControlIndex].iAreaArray.Close(); + + iControlCache.Remove( aControlIndex ); + } + +// --------------------------------------------------------------------------- +// We have a separate function for knowing if control's feedback areas +// should be active or not, because determining this is not so simple, and +// because this part can still change. +// +// Control's areas are NOT active in case +// +// #1 Its feedback has been explicitely disabled for audio and vibra +// +// OR +// +// #2 It is not visible +// +// OR +// +// #3 It is dimmed. +// +// --------------------------------------------------------------------------- +// +TBool CTouchFeedbackImpl::ControlsAreasActive( TInt aControlIndex ) + { + __ASSERT_ALWAYS( + aControlIndex >= 0 && aControlIndex < iControlCache.Count(), + Panic( ETouchClientPanicArrayAccess ) ); + + TBool ret = ETrue; + + if ( ( iControlCache[aControlIndex].iVibraDisabled && // #1 + iControlCache[aControlIndex].iAudioDisabled ) || + !iControlCache[aControlIndex].iVisible || // #2 + iControlCache[aControlIndex].iDimmed ) // #3 + { + ret = EFalse; + } + + return ret; + } + +// --------------------------------------------------------------------------- +// This function can be used for disabling or enabling all control's +// feedback areas in the registry. +// +// #1 Find out the registry index. +// #2 In a loop, disable / enable each feedback area for this control +// #3 Maintain bookkeeping, so that we will know in the end if anything +// changed or not (so that we won't make unnecessary updates to server) +// #4 Make a pending update request if registry really changed. +// --------------------------------------------------------------------------- +// +void CTouchFeedbackImpl::DoEnableControlsAreasInRegistry( + TInt aControlIndex, + TBool aEnableVibra, + TBool aEnableAudio, + TBool aVisible ) + { + __ASSERT_ALWAYS( + aControlIndex >= 0 && aControlIndex < iControlCache.Count(), + Panic( ETouchClientPanicArrayAccess ) ); + + TBool registryChanged = EFalse; + + TControlCacheEntry& entry ( iControlCache[aControlIndex] ); + + // #1 + TInt registryIndex = FindWindowFromRegistry ( entry.iClientHandle ); + + if ( registryIndex != KErrNotFound ) + { + for ( TInt i=0; i < entry.iAreaArray.Count(); i++ ) + { + TInt id = entry.iAreaArray[i].iAreaId; + + // #2 + TBool changedNow = + iRegistryArray[registryIndex]->SetFeedbackEnabled( + id, aEnableVibra, aEnableAudio, aVisible ); + + // #3 + registryChanged = registryChanged || changedNow; + } + } + + // #4 + if ( registryChanged ) + { + iClient->RegistryChanged(); + } + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +EXPORT_C CFeedbackSpec* CFeedbackSpec::New() + { + return new CFeedbackSpec; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +CFeedbackSpec::~CFeedbackSpec() + { + iFbArray.Close(); + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +EXPORT_C TInt CFeedbackSpec::AddFeedback( TTouchEventType aEventType, + TTouchLogicalFeedback aFeedback ) + { + // Vibra feedback is enabled by default for every event type. + TInt fbType( ETouchFeedbackVibra | ETouchFeedbackAudio ); + + return AddFeedback( aEventType, + aFeedback, + static_cast( fbType ) ); + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +EXPORT_C TInt CFeedbackSpec::AddFeedback( TTouchEventType aEventType, + TTouchLogicalFeedback aFeedback, + TTouchFeedbackType aFeedbackType ) + { + TTactileFbItem item; + if ( !(aEventType >= ETouchEventStylusDown && aEventType <= ETouchEventStylusPressUp ) ) + { + return KErrArgument; + } + item.iEventType = aEventType; + + // range check. update when logical feedback types are changed. + if (! ( aFeedback >= ETouchFeedbackNone || aFeedback <= ETouchFeedbackSensitive) + || ! ( aFeedback >= ETouchFeedbackBasicButton || + aFeedback <= ETouchFeedbackMultiTouchRecognized ) ) + { + return KErrArgument; + } + item.iFeedback = aFeedback; + item.iFeedbackType = aFeedbackType; + + iFbArray.Append(item); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CFeedbackSpec::GetFeedbackSpec( RArray& aArray ) + { + // copy values from one array to another + TInt count = iFbArray.Count(); + aArray.Reset(); // remove records from array if any + + for ( TInt i=0 ; i < count ; i++ ) + { + TTactileFbItem item; + item.iFeedback = iFbArray[i].iFeedback; + item.iEventType = iFbArray[i].iEventType; + item.iFeedbackType = iFbArray[i].iFeedbackType; + aArray.Append(item); + } + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +CFeedbackSpec::CFeedbackSpec() + { + } + +// End of File