--- /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 <eikenv.h>
+#include <coecntrl.h>
+#include <featmgr.h>
+#include <aknutils.h>
+#include <tactilefeedbacktrace.h>
+#include <touchfeedbackspec.h>
+
+#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<TControlCacheEntry>* array =
+ static_cast<RArray<TControlCacheEntry>*> ( 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<TAreaIndexEntry>* array =
+ static_cast<RArray<TAreaIndexEntry>*> ( 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<TTouchLogicalFeedback>( 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<CTouchFeedbackRegistry>* 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<TTouchFeedbackType>(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<CCoeControl*>( 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<TTactileFbItem> 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<feedbackItems;i++)
+ {
+ TTactileFbItem item = feedbackEntries[i];
+ eventType = item.iEventType;
+
+ if ( item.iEventType == ETouchEventStylusDown )
+ {
+ feedbackTypeDown = item.iFeedback;
+ feedbackDown = item.iFeedbackType;
+ }
+ else if ( item.iEventType == ETouchEventStylusUp )
+ {
+ feedbackTypeUp = item.iFeedback;
+ feedbackUp = item.iFeedbackType;
+ }
+ }
+ if ( feedbackItems > 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<TFeedbackEntry>* 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<TUint32>( 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<TTouchFeedbackType>( 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<TTactileFbItem>& 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