webengine/webkitutils/stmgesturefw/src/stateengine.cpp
changeset 65 5bfc169077b2
parent 42 d39add9822e2
child 66 cacf6ee57968
equal deleted inserted replaced
42:d39add9822e2 65:5bfc169077b2
     1 /*
       
     2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of the License "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include "stateengine.h"
       
    20 #include "utils.h"
       
    21 #include "uievent.h"
       
    22 #include "uieventsender.h"
       
    23 #include "filelogger.h"
       
    24 //#include "flogger.h"
       
    25 
       
    26 using namespace stmUiEventEngine ;
       
    27 
       
    28 /*!
       
    29   State definitions glue together the methods of the stateengine.cpp
       
    30   so that it will behave as defined in the state machine specification.
       
    31   First define the separate elements for each possible event and then tie them together
       
    32   to create one state entry.  The state entries then are put to array
       
    33   where the index is at the same time also the state ID.
       
    34 
       
    35   STATE_ELEMENT arrays define the methods called when handling a message.
       
    36   \sa STATE_ELEMENT.
       
    37   Note that the last row of STATE_ELEMENT array must NOT have a ConditionFunction entry
       
    38   and it must have a NextState entry != Eignore.  Otherwise the state machine will
       
    39   not behave correctly.
       
    40 
       
    41  */
       
    42 /*! Add macro with token pasting to make creation of the state machine tables easier
       
    43    and removing the necessity to write the classname twice.
       
    44    Maybe there would be some better way to do this using templates and typedefs?
       
    45  */
       
    46 #define CND(x) isF<CStateEngine,&CStateEngine::##x>
       
    47 #define ACT(x) aF<CStateEngine,&CStateEngine::##x>
       
    48 
       
    49 /*********************************************************************************
       
    50  * empty statedef as a dummy entry
       
    51  *  */
       
    52 const STATE_ELEMENT __ErrorEvent[1] = {
       
    53         0,
       
    54         ACT(ErrorEvent),
       
    55         EInit
       
    56 };
       
    57 
       
    58 const STATE Ignore__[1] = {
       
    59         EDown,          __ErrorEvent
       
    60 } ;
       
    61 
       
    62 /*!
       
    63   :INIT state and its event specific elements
       
    64   See the spec in http://wikis.in.nokia.com/Runtimes/NewGestureLibrary
       
    65   Down is only valid event in :INIT state
       
    66   The event is consumed immediately, so that the state machine will process only these
       
    67   methods when processing the message.
       
    68   If touch timer has been set, the next state is InTouchTime.
       
    69   If no touch timer, but if hold timer has been defined, the next state is InHoldTime_U
       
    70   If no touch or hold timer have been defined, but touch area has been defined, next state is InTouchArea.
       
    71   11-May-2009: addition: add another touch area: one for touch time and one for touch area after touch time
       
    72   has elapsed.  This allows "sloppy" touch to be handled properly without extra move if touchtimearea is larger,
       
    73   but after touch has been detected a smaller movement is allowed.
       
    74  */
       
    75 const STATE_ELEMENT Init__Down[12] = {
       
    76         0,                  ACT(ConsumeEvent),    Eignore,
       
    77         0,                  ACT(SetGestureStart), Eignore,
       
    78         0,                  ACT(SetCurrentPos),   Eignore,
       
    79         CND(IsTouchTimer),  ACT(InitTouchTimer),  Eignore,
       
    80         CND(IsHoldTimer),   ACT(InitHoldTimer),   Eignore,
       
    81         CND(IsTouchTimeArea),   ACT(PrepareTouchTimeArea),Eignore,
       
    82         CND(IsHoldArea),    ACT(PrepareHoldArea) ,Eignore,
       
    83         CND(IsTouchTimer),  0,                    EInTouchTime,
       
    84         0,                  ACT(ProduceTouch),    Eignore,
       
    85         CND(IsHoldTimer),   0,                    EInHoldTime_U,
       
    86         CND(IsTouchArea),   ACT(PrepareTouchArea),EInTouchArea,
       
    87         0,                  0,                    EDispatch             // If nothing else happens, goto to Dispatch state
       
    88 };
       
    89 /**
       
    90  * All the rest of the events are errors so produce error entry to log and
       
    91  * stay in the Init state
       
    92  */
       
    93 const STATE_ELEMENT Init__ErrorEvent[2] = {
       
    94         0,              ACT(ConsumeEvent),    Eignore,  // remember to consume event, otherwise state machine will loop...
       
    95         0,              ACT(ErrorEvent),      EInit
       
    96 };
       
    97 
       
    98 /*!
       
    99  * :INIT
       
   100  * note that only valid event is DOWN, all the rest can be handled as errors
       
   101  */
       
   102 const STATE Init__[7] = {
       
   103         EDown,          Init__Down,
       
   104         EDrag,          Init__ErrorEvent,
       
   105         ECapacitiveUP,  Init__ErrorEvent,
       
   106         EResistiveUP,   Init__ErrorEvent,
       
   107         ETouchTimer,    Init__ErrorEvent,
       
   108         EHoldTimer,     Init__ErrorEvent,
       
   109         ESuppressTimer, Init__ErrorEvent
       
   110 };
       
   111 
       
   112 /*!
       
   113  *  :Dispatch state end its elements
       
   114  *  Here the valid events are DRAG and the UP events.
       
   115  */
       
   116 const STATE_ELEMENT Dispatch__Drag[7] = {
       
   117         0,                  ACT(StoreMovePos),    Eignore,
       
   118         0,                  ACT(ConsumeEvent),    Eignore,
       
   119         0,                  ACT(SetCurrentPos),   Eignore,
       
   120         0,                  ACT(AddDraggingPos),  Eignore,
       
   121         0,                  ACT(ProduceMove),     Eignore,
       
   122         CND(LooksLikeHold), ACT(InitHoldTimer),EInHoldTime_U,
       
   123         0,                  0,                    EDispatch
       
   124 } ;
       
   125 
       
   126 const STATE_ELEMENT Dispatch__CapacitiveUp[3] = {
       
   127         0,                  ACT(ConsumeEvent),    Eignore,
       
   128         0,                  ACT(SetCurrentPos),   Eignore,
       
   129         0,                  ACT(ProduceRelease),  EInit
       
   130 } ;
       
   131 
       
   132 const STATE_ELEMENT Dispatch__ResistiveUp[4] = {
       
   133         0,                   ACT(ConsumeEvent),       Eignore,
       
   134         0,                   ACT(SetCurrentPos),   Eignore,
       
   135         CND(IsSuppressTimer),ACT(InitMoveSuppressTimer),  ESuppress_D,
       
   136         0,                   ACT(ProduceRelease),     EInit
       
   137 } ;
       
   138 
       
   139 /*!
       
   140  * All the rest of the events are errors so produce error entry to log and
       
   141  * stay in the Dispatch state
       
   142  * (TODO: note that in the future we may further
       
   143  * define the error cases so that they may change state; )
       
   144  */
       
   145 const STATE_ELEMENT DispatchErrorEvent[2] = {
       
   146         0,              ACT(ConsumeEvent),    Eignore,  // remember to consume event, otherwise state machine will loop...
       
   147         0,              ACT(ErrorEvent),      EDispatch
       
   148 };
       
   149 
       
   150 const STATE Dispatch__[7] = {
       
   151         EDown,          DispatchErrorEvent,
       
   152         EDrag,          Dispatch__Drag,
       
   153         ECapacitiveUP,  Dispatch__CapacitiveUp,
       
   154         EResistiveUP,   Dispatch__ResistiveUp,
       
   155         ETouchTimer,    DispatchErrorEvent,
       
   156         EHoldTimer,     DispatchErrorEvent,
       
   157         ESuppressTimer, DispatchErrorEvent
       
   158 };
       
   159 
       
   160 /*!
       
   161  *  :InTouchTime state end its elements
       
   162  *  Here the valid events are DRAG and the UP events and the TouchTimer
       
   163  */
       
   164 const STATE_ELEMENT InTouchTime__Drag[6] = {
       
   165         0,                  ACT(StoreMovePos),    Eignore,
       
   166         CND(InsideTouchTimeArea), ACT(ConsumeEvent),      Eignore,
       
   167         CND(InsideTouchTimeArea), ACT(AddToTouch),        EInTouchTime,     // Calculate touch XY as average of the touches
       
   168         0,                    ACT(ClearTouchTimer),   Eignore,          // These lines are done only if InsideTouchArea returns false
       
   169         0,                    ACT(ClearHoldTimer),    Eignore,
       
   170         0,                    ACT(ProduceTouch),      EDispatch
       
   171 
       
   172 } ;
       
   173 /**
       
   174  * Note that ConsumeEvent is missing so after doing this the state engine will do EDispatch
       
   175  */
       
   176 const STATE_ELEMENT InTouchTime__CapacitiveUp[4] = {
       
   177         0,                  ACT(SetCurrentPos),       Eignore,
       
   178         0,                  ACT(ClearTouchTimer),     Eignore,
       
   179         0,                  ACT(ClearHoldTimer),      Eignore,
       
   180         0,                  ACT(ProduceTouch),        EDispatch
       
   181 } ;
       
   182 /**
       
   183  * Note that ConsumeEvent is not called if IsHoldTimer returns false, so the Dispatch will be done
       
   184  * by the state machine.
       
   185  */
       
   186 const STATE_ELEMENT InTouchTime__ResistiveUp[5] = {
       
   187         0,                   ACT(SetCurrentPos),   Eignore,
       
   188         0,                   ACT(ClearTouchTimer),    Eignore,
       
   189         0,                   ACT(ProduceTouch),       Eignore,
       
   190         CND(IsHoldTimer),    0, /*ACT(ConsumeEvent),*/EInHoldTime_U,    // Note that otherwise immediate UP is handled improperly
       
   191         0,                   0,                       EDispatch
       
   192 } ;
       
   193 
       
   194 const STATE_ELEMENT InTouchTime__TouchTimer[6] = {
       
   195         0,                   ACT(ConsumeEvent),       Eignore,
       
   196         0,                   ACT(ClearTouchTimer),    Eignore,
       
   197         0,                   ACT(ProduceTouch),       Eignore,
       
   198         CND(IsTouchArea),    ACT(PrepareTouchArea),Eignore,         // prepare the other touch area
       
   199         CND(IsHoldTimer),    0,                       EInHoldTime_U,
       
   200         0,                   0,                     EInTouchArea
       
   201 } ;
       
   202 
       
   203 
       
   204 /**
       
   205  * All the rest of the events are errors so produce error entry to log and
       
   206  * stay in the InTouchTime state
       
   207  */
       
   208 const STATE_ELEMENT InTouchTimeErrorEvent[2] = {
       
   209         0,              ACT(ConsumeEvent),    Eignore,  // remember to consume event, otherwise state machine will loop...
       
   210         0,              ACT(ErrorEvent),      EInTouchTime
       
   211 };
       
   212 
       
   213 const STATE InTouchTime__[7] = {
       
   214         EDown,          InTouchTimeErrorEvent,
       
   215         EDrag,          InTouchTime__Drag,
       
   216         ECapacitiveUP,  InTouchTime__CapacitiveUp,
       
   217         EResistiveUP,   InTouchTime__ResistiveUp,
       
   218         ETouchTimer,    InTouchTime__TouchTimer,
       
   219         EHoldTimer,     InTouchTimeErrorEvent,
       
   220         ESuppressTimer, InTouchTimeErrorEvent
       
   221 };
       
   222 
       
   223 /*!
       
   224  *  :InHoldTime_U state end its elements
       
   225  *  Here only touch timer event is invalid
       
   226  */
       
   227 
       
   228 const STATE_ELEMENT InHoldTime_U__Down[1] = {
       
   229         0,                    0,      EInHoldTime_D  // Note that ConsumeEvent is not called
       
   230 } ;
       
   231 
       
   232 const STATE_ELEMENT InHoldTime_U__Drag[3] = {
       
   233         0,                  ACT(StoreMovePos),    Eignore,
       
   234         CND(InsideHoldArea), ACT(ConsumeEvent),       EInHoldTime_U,
       
   235         0,                    ACT(ClearHoldTimer),    EDispatch     // Note that in this case ConsumeEvent is not called
       
   236 } ;
       
   237 /**
       
   238  * Note that ConsumeEvent is missing so after doing this the state engine will do EDispatch
       
   239  */
       
   240 const STATE_ELEMENT InHoldTime_U__CapacitiveUp[1] = {
       
   241         0,                  ACT(ClearHoldTimer),      EDispatch     // Note that ConsumeEvent not called
       
   242 } ;
       
   243 /**
       
   244  *
       
   245  */
       
   246 const STATE_ELEMENT InHoldTime_U__ResistiveUp[5] = {
       
   247         0,                   ACT(ConsumeEvent),  Eignore,
       
   248         0,                   ACT(SetCurrentPos),   Eignore,
       
   249         CND(IsSuppressTimer),ACT(InitTouchSuppressTimer),  EInHoldTime_D,   // If suppression, start timer and wait for down or timer
       
   250         0,                   ACT(ClearHoldTimer), Eignore,  // remember to do this
       
   251         0,                   ACT(ProduceRelease), EInit     // No suppression, then this is immediate release
       
   252 } ;
       
   253 
       
   254 const STATE_ELEMENT InHoldTime_U__HoldTimer[3] = {
       
   255         0,                   ACT(ConsumeEvent),       Eignore,
       
   256         0,                   ACT(ProduceHold),        Eignore,
       
   257         0,                   ACT(RestartHoldTimer),   EInHoldTime_U,
       
   258 } ;
       
   259 
       
   260 const STATE_ELEMENT InHoldTime_U__SuppressTimer[2] = {
       
   261         0,              ACT(ConsumeEvent),    Eignore,  // remember to consume event, otherwise state machine will loop...
       
   262         0,              ACT(ErrorEvent),      EInHoldTime_U
       
   263 } ;
       
   264 
       
   265 
       
   266 /**
       
   267  * All the rest of the events are errors so produce error entry to log and
       
   268  * stay in the InHoldTime_U state
       
   269  */
       
   270 const STATE_ELEMENT InHoldTime_UErrorEvent[2] = {
       
   271         0,              ACT(ConsumeEvent),    Eignore,  // remember to consume event, otherwise state machine will loop...
       
   272         0,              ACT(ErrorEvent),      EInHoldTime_U
       
   273 };
       
   274 
       
   275 const STATE InHoldTime_U__[7] = {
       
   276         EDown,          InHoldTime_U__Down,
       
   277         EDrag,          InHoldTime_U__Drag,
       
   278         ECapacitiveUP,  InHoldTime_U__CapacitiveUp,
       
   279         EResistiveUP,   InHoldTime_U__ResistiveUp,
       
   280         ETouchTimer,    InHoldTime_UErrorEvent,
       
   281         EHoldTimer,     InHoldTime_U__HoldTimer,
       
   282         ESuppressTimer, InHoldTime_U__SuppressTimer
       
   283 };
       
   284 
       
   285 
       
   286 /*!
       
   287  *  :InHoldTime_D state end its elements
       
   288  *  Here drag, touch timer and suppress timer events are invalid
       
   289  */
       
   290 
       
   291 const STATE_ELEMENT InHoldTime_D__Down[5] = {
       
   292         0,                    ACT(ClearSuppressTimer),    Eignore,
       
   293         0,                    ACT(ConsumeEvent),      Eignore,
       
   294         CND(InsideHoldArea),  0,                      EInHoldTime_U,
       
   295         0,                    ACT(ClearHoldTimer),    Eignore,
       
   296         0,                    ACT(ProduceMove),       EDispatch
       
   297 } ;
       
   298 
       
   299 /**
       
   300  * Note that ConsumeEvent is missing so after doing this the state engine will do InHoldTime_U
       
   301  */
       
   302 const STATE_ELEMENT InHoldTime_D__CapacitiveUp[1] = {
       
   303         0,                  0,    EInHoldTime_U
       
   304 } ;
       
   305 /**
       
   306  * Note that ConsumeEvent is missing so after doing this the state engine will do InHoldTime_U
       
   307  */
       
   308 const STATE_ELEMENT InHoldTime_D__ResistiveUp[1] = {
       
   309         0,                  0,    EInHoldTime_U     // InHoldTime_U initialises timers etc. if needed
       
   310 } ;
       
   311 /*!
       
   312  * In case of hold timer has been elapsed stop the timers, generate Release UI event.
       
   313  */
       
   314 const STATE_ELEMENT InHoldTime_D__HoldTimer[4] = {
       
   315         0,                   ACT(ConsumeEvent),       Eignore,
       
   316         0,                   ACT(ClearSuppressTimer), Eignore,
       
   317         0,                   ACT(ClearHoldTimer),     Eignore,
       
   318         0,                   ACT(ProduceRelease),     EInit,
       
   319 } ;
       
   320 /*!
       
   321  * If suppress timer hits, stop the timers and generate Release UI event.
       
   322  */
       
   323 const STATE_ELEMENT InHoldTime_D__SuppressTimer[4] = {
       
   324         0,                   ACT(ConsumeEvent),       Eignore,
       
   325         0,                   ACT(ClearSuppressTimer), Eignore,
       
   326         0,                   ACT(ClearHoldTimer),     Eignore,
       
   327         0,                   ACT(ProduceRelease),     EInit,
       
   328 } ;
       
   329 
       
   330 /**
       
   331  * All the rest of the events are errors so produce error entry to log and
       
   332  * stay in the InHoldTime_D state
       
   333  */
       
   334 const STATE_ELEMENT InHoldTime_DErrorEvent[2] = {
       
   335         0,              ACT(ConsumeEvent),    Eignore,  // remember to consume event, otherwise state machine will loop...
       
   336         0,              ACT(ErrorEvent),      EInHoldTime_D
       
   337 };
       
   338 
       
   339 const STATE InHoldTime_D__[7] = {
       
   340         EDown,          InHoldTime_D__Down,
       
   341         EDrag,          InHoldTime_DErrorEvent,
       
   342         ECapacitiveUP,  InHoldTime_D__CapacitiveUp,
       
   343         EResistiveUP,   InHoldTime_D__ResistiveUp,
       
   344         ETouchTimer,    InHoldTime_DErrorEvent,
       
   345         EHoldTimer,     InHoldTime_D__HoldTimer,
       
   346         ESuppressTimer, InHoldTime_D__SuppressTimer
       
   347 };
       
   348 
       
   349 
       
   350 /*!
       
   351  *  :InTouchArea state end its elements
       
   352  *  Here Drag and Up events are valid.
       
   353  *  If drag is inside touch are it is ignored, otherwise
       
   354  *  the Dispatch state will handle the event.
       
   355  */
       
   356 
       
   357 const STATE_ELEMENT InTouchArea__Drag[3] = {
       
   358         0,                  ACT(StoreMovePos),    Eignore,
       
   359         CND(InsideTouchArea), ACT(ConsumeEvent),     EInTouchArea,
       
   360         0,                    0,                     EDispatch  // Note that in this case ConsumeEvent has not been called so Dispatch state processes the message
       
   361 } ;
       
   362 
       
   363 /**
       
   364  * Note that ConsumeEvent is missing so after doing this the state engine will do Dispatch
       
   365  */
       
   366 const STATE_ELEMENT InTouchArea__CapacitiveUp[1] = {
       
   367         0,                  0,    EDispatch
       
   368 } ;
       
   369 /**
       
   370  * Note that ConsumeEvent is missing so after doing this the state engine will do Dispatch
       
   371  */
       
   372 const STATE_ELEMENT InTouchArea__ResistiveUp[1] = {
       
   373         0,                  0,    EDispatch
       
   374 } ;
       
   375 
       
   376 /**
       
   377  * All the rest of the events are errors so produce error entry to log and
       
   378  * stay in the InTouchArea state
       
   379  */
       
   380 const STATE_ELEMENT InTouchAreaErrorEvent[2] = {
       
   381         0,              ACT(ConsumeEvent),    Eignore,  // remember to consume event, otherwise state machine will loop...
       
   382         0,              ACT(ErrorEvent),      EInTouchArea
       
   383 };
       
   384 
       
   385 const STATE InTouchArea__[7] = {
       
   386         EDown,          InTouchAreaErrorEvent,
       
   387         EDrag,          InTouchArea__Drag,
       
   388         ECapacitiveUP,  InTouchArea__CapacitiveUp,
       
   389         EResistiveUP,   InTouchArea__ResistiveUp,
       
   390         ETouchTimer,    InTouchAreaErrorEvent,
       
   391         EHoldTimer,     InTouchAreaErrorEvent,
       
   392         ESuppressTimer, InTouchAreaErrorEvent
       
   393 };
       
   394 
       
   395 
       
   396 /*!
       
   397  *  :Suppress_D state end its elements
       
   398  *  Here Down and suppress timers are OK.
       
   399  */
       
   400 
       
   401 /*!
       
   402  * Down will be handled as a Drag event in the Dispatch state.
       
   403  */
       
   404 const STATE_ELEMENT Suppress_D__Down[4] = {
       
   405         0,           ACT(ClearSuppressTimer),    Eignore,
       
   406         0,           ACT(RenameToDrag),          EDispatch
       
   407 } ;
       
   408 /*!
       
   409  * Suppress timer will generate Release UI event.
       
   410  */
       
   411 const STATE_ELEMENT Suppress_D__SuppressTimer[3] = {
       
   412         0,                   ACT(ConsumeEvent),       Eignore,
       
   413         0,                   ACT(ClearSuppressTimer), Eignore,
       
   414         0,                   ACT(ProduceRelease),     EInit,
       
   415 } ;
       
   416 
       
   417 /**
       
   418  * All the rest of the events are errors so produce error entry to log and
       
   419  * stay in the Suppress_D state
       
   420  */
       
   421 const STATE_ELEMENT Suppress_DErrorEvent[2] = {
       
   422         0,              ACT(ConsumeEvent),    Eignore,  // remember to consume event, otherwise state machine will loop...
       
   423         0,              ACT(ErrorEvent),      ESuppress_D
       
   424 };
       
   425 
       
   426 const STATE Suppress_D__[7] = {
       
   427         EDown,          Suppress_D__Down,
       
   428         EDrag,          Suppress_DErrorEvent,
       
   429         ECapacitiveUP,  Suppress_DErrorEvent,
       
   430         EResistiveUP,   Suppress_DErrorEvent,
       
   431         ETouchTimer,    Suppress_DErrorEvent,
       
   432         EHoldTimer,     Suppress_DErrorEvent,
       
   433         ESuppressTimer, Suppress_D__SuppressTimer
       
   434 };
       
   435 /*!
       
   436  * The allStates array contains all the possible states of the state machine.
       
   437  */
       
   438 const STATE* const allStates[8] =
       
   439 {
       
   440         Ignore__,
       
   441         Init__,
       
   442         Dispatch__,
       
   443         InTouchTime__,
       
   444         InHoldTime_U__,
       
   445         InHoldTime_D__,
       
   446         InTouchArea__,
       
   447         Suppress_D__
       
   448 };
       
   449 /*!
       
   450  * stateNames are used in the logging
       
   451  */
       
   452 const char* const stateNames[8] =
       
   453 {
       
   454         "Ignore",
       
   455         "Init",
       
   456         "Dispatch",
       
   457         "InTouchTime",
       
   458         "InHoldTime_U",
       
   459         "InHoldTime_D",
       
   460         "InTouchArea",
       
   461         "Suppress"
       
   462 };
       
   463 
       
   464 // event names are also used in logging
       
   465 const char* const hweventNames[] = {
       
   466         "EDown",
       
   467         "EDrag",
       
   468         "ECapacitiveUP",
       
   469         "EResistiveUP",
       
   470         "ETouchTimer",
       
   471         "EHoldTimer",
       
   472         "ESuppressTimer"
       
   473 } ;
       
   474 
       
   475 /*! CStateEngine contains the methods used in the state machine implementation.
       
   476  *
       
   477  *  The methods in CStateEngine used in the state machine definition are
       
   478  *  either condition methods or action methods.
       
   479  *
       
   480  *  Constructor
       
   481  *  \param[in]: MTimerInterface atimerif.  An attempt to make this more OS agnostic the actual
       
   482  *  timers are accessed using a separate interface.
       
   483  */
       
   484 CStateEngine::CStateEngine(CStateEngineConfiguration* aConfig, MTimerInterface* atimerif, int aIndex)
       
   485 {
       
   486     m_config = aConfig ;
       
   487     m_timerif = atimerif ;
       
   488     m_currentState = EInit ;
       
   489     m_index = aIndex ;
       
   490 }
       
   491 
       
   492 CStateEngine::~CStateEngine()
       
   493 {
       
   494     // Just to be sure...
       
   495     iTouchPoints.Reset() ;
       
   496     iDragPoints.ResetAndDestroy() ;
       
   497 }
       
   498 /*!
       
   499  * ConsumeEvent: the method defines that the turnStateMachine will stop the processing
       
   500  * of the state methods after it has reached the next state.
       
   501  *
       
   502  */
       
   503 void CStateEngine::ConsumeEvent()
       
   504 {
       
   505     m_eventConsumed = true ;
       
   506 }
       
   507 /*!
       
   508  * Condition method
       
   509  * \return true, if the touch timer limit > 0
       
   510  */
       
   511 bool CStateEngine::IsTouchTimer()
       
   512 {
       
   513     bool isit =  (m_config->m_touchTimerLimit > 0) ;
       
   514 
       
   515     return isit ;
       
   516 }
       
   517 /*!
       
   518  * Condition method
       
   519  * \return true, if the hold timer limit > 0
       
   520  */
       
   521 bool CStateEngine::IsHoldTimer()
       
   522 {
       
   523     bool isit =  (m_config->m_holdTimerLimit > 0) ;
       
   524 
       
   525     return isit ;
       
   526 }
       
   527 /*!
       
   528  * Condition method
       
   529  * \return true, if the suppress timer limit > 0
       
   530  */
       
   531 bool CStateEngine::IsSuppressTimer()
       
   532 {
       
   533     bool isit =  (m_config->m_suppressTimerLimit > 0) ;
       
   534 
       
   535     return isit ;
       
   536 }
       
   537 /*!
       
   538  * Condition method
       
   539  * \return true, if the touch area has been defined (the touch tolerancelength > 0)
       
   540  */
       
   541 bool CStateEngine::IsTouchTimeArea()
       
   542 {
       
   543     bool isit = (m_config->m_touchTimeTolerance.iX > 0) ;
       
   544     return isit  ;
       
   545 }
       
   546 /*!
       
   547  * Condition method
       
   548  * \return true, if the touch area has been defined (the touch tolerancelength > 0)
       
   549  */
       
   550 bool CStateEngine::IsTouchArea()
       
   551 {
       
   552     bool isit = (m_config->m_touchTolerance.iX > 0) ;
       
   553     return isit  ;
       
   554 }
       
   555 /*!
       
   556  * Condition method
       
   557  * \return true, if the hold area has been defined (the hold tolerancelength > 0)
       
   558  */
       
   559 bool CStateEngine::IsHoldArea()
       
   560 {
       
   561     bool isit = (m_config->m_holdTolerance.iX > 0) ;
       
   562     return isit  ;
       
   563 }
       
   564 
       
   565 bool CStateEngine::InsideArea(const TPoint& point,
       
   566                               const TRect& rect,
       
   567                               TAreaShape shape,
       
   568                               const TPoint& tolerance)
       
   569 {
       
   570     bool isit;
       
   571     switch(shape)
       
   572     {
       
   573     default:    // pass trough
       
   574     case ERectangle:
       
   575     {
       
   576         isit = rect.Contains(m_hwe.iPosition) ;
       
   577         break ;
       
   578     }
       
   579     case ECircle:
       
   580     {
       
   581         TPoint delta = m_hwe.iPosition - point;
       
   582         long circlepoint = delta.iX * delta.iX + delta.iY * delta.iY;
       
   583         isit = (circlepoint < tolerance.iX * tolerance.iX);
       
   584         break ;
       
   585     }
       
   586     case EEllipse:
       
   587     {
       
   588         int asquare = tolerance.iX * tolerance.iX ;
       
   589         int bsquare = tolerance.iY * tolerance.iY ;
       
   590         TPoint delta = m_hwe.iPosition - point;
       
   591         int result = (delta.iX * delta.iX) * bsquare + (delta.iY * delta.iY) * asquare;
       
   592 
       
   593         isit = (result < asquare * bsquare);
       
   594         break ;
       
   595     }
       
   596     }
       
   597     return isit ;
       
   598 }
       
   599 
       
   600 /*!
       
   601  * Condition method
       
   602  * Check if the current event is positioned inside the touch area.
       
   603  * Touch area can be a rectangle, a circle or an ellipse, so different
       
   604  * calculation needs to be done based on the shape of the area.
       
   605  */
       
   606 bool CStateEngine::InsideTouchTimeArea()
       
   607 {
       
   608     return InsideArea(m_touchCentre, m_touchRect,
       
   609                       m_config->m_touchAreaShape, m_config->m_touchTimeTolerance);
       
   610 }
       
   611 /*!
       
   612  * Condition method
       
   613  * Check if the current event is positioned inside the touch area.
       
   614  * Touch area can be a rectangle, a circle or an ellipse, so different
       
   615  * calculation needs to be done based on the shape of the area.
       
   616  */
       
   617 bool CStateEngine::InsideTouchArea()
       
   618 {
       
   619     return InsideArea(m_touchCentre, m_touchRect,
       
   620                       m_config->m_touchAreaShape, m_config->m_touchTolerance);
       
   621 }
       
   622 /*!
       
   623  * Condition method
       
   624  * Check if the current event is positioned inside the hold area.
       
   625  * Hold area can be a rectangle, a circle or an ellipse, so different
       
   626  * calculation needs to be done based on the shape of the area.
       
   627  */
       
   628 bool CStateEngine::InsideHoldArea()
       
   629 {
       
   630     return InsideArea(m_holdCentre, m_holdRect,
       
   631                       m_config->m_holdAreaShape, m_config->m_holdTolerance);
       
   632 }
       
   633 /*!
       
   634  * Condition method
       
   635  * Check if the gesture looks like a hold, i.e. the movement has stopped.
       
   636  * \sa isNewHoldingPoint
       
   637  */
       
   638 bool CStateEngine::LooksLikeHold()
       
   639 {
       
   640     bool isit = isNewHoldingPoint() ;
       
   641     return isit ;
       
   642 }
       
   643 /*!
       
   644  * Action method
       
   645  * Error logging.
       
   646  */
       
   647 void CStateEngine::ErrorEvent()
       
   648 {
       
   649     // Log the error
       
   650     if (m_config->m_enableLogging)
       
   651     {
       
   652         LOGARG("ErrorEvent: %s %s", stateNames[m_currentState], hweventNames[m_hwe.iType]) ;
       
   653     }
       
   654 }
       
   655 /*!
       
   656  * Action method
       
   657  * Initialize touch timer.  At the same time calculate also the touch rectangle.
       
   658  */
       
   659 void CStateEngine::InitTouchTimer()
       
   660 {
       
   661     m_touchRect = ToleranceRect(m_hwe.iPosition, m_config->m_touchTolerance) ;
       
   662     m_touchCentre = m_hwe.iPosition ;
       
   663     m_timerif->startTouchTimer(m_config->m_touchTimerLimit, m_index) ;
       
   664 }
       
   665 /*!
       
   666  * Action method.
       
   667  * Initialize hold timer.  At the same time calculate also the hold rectangle.
       
   668  */
       
   669 void CStateEngine::InitHoldTimer()
       
   670 {
       
   671     m_holdRect = ToleranceRect(m_hwe.iPosition, m_config->m_holdTolerance) ;
       
   672     m_holdCentre = m_hwe.iPosition ;
       
   673     m_timerif->startHoldTimer(m_config->m_holdTimerLimit, m_index) ;
       
   674 }
       
   675 /*!
       
   676  * Action method
       
   677  * Restart the hold timer using the hold timer limit.
       
   678  */
       
   679 void CStateEngine::RestartHoldTimer()
       
   680 {
       
   681     m_timerif->startHoldTimer(m_config->m_holdTimerLimit, m_index) ;
       
   682 }
       
   683 /*!
       
   684  * Action method
       
   685  * Initialize suppression timer.  This timer is used during touch detection when
       
   686  * resistive UP has been detected.  If new DOWN comes while timer is running, it is ignored.
       
   687  */
       
   688 void CStateEngine::InitTouchSuppressTimer()
       
   689 {
       
   690     m_timerif->startSuppressTimer(m_config->m_suppressTimerLimit, m_index) ;
       
   691 }
       
   692 /*!
       
   693  * Action method.
       
   694  * Initialize suppression timer after move.  Tests show that when user is using light touch and
       
   695  * moving finger to opposite directions there may be accidental ups and downs where the time between
       
   696  * up and down may be well over 120 ms.
       
   697  */
       
   698 void CStateEngine::InitMoveSuppressTimer()
       
   699 {
       
   700     m_timerif->startSuppressTimer(m_config->m_moveSuppressTimerLimit, m_index) ;
       
   701 }
       
   702 /*!
       
   703  * Action method
       
   704  * Stop the touch timer.
       
   705  */
       
   706 void CStateEngine::ClearTouchTimer()
       
   707 {
       
   708     m_timerif->cancelTouchTimer(m_index) ;
       
   709 }
       
   710 /*!
       
   711  * Action method
       
   712  * Stop the hold timer.
       
   713  */
       
   714 void CStateEngine::ClearHoldTimer()
       
   715 {
       
   716     m_timerif->cancelHoldTimer(m_index) ;
       
   717 }
       
   718 /*!
       
   719  * Action method
       
   720  * Stop the suppress timer.
       
   721  */
       
   722 void CStateEngine::ClearSuppressTimer()
       
   723 {
       
   724     m_timerif->cancelSuppressTimer(m_index) ;
       
   725 }
       
   726 /*!Helper method.
       
   727  * Create UI event
       
   728  * \param code The new UI event type (Touch, Release, Move, Hold)
       
   729  */
       
   730 CUiEvent* CStateEngine::createUIEventL(TUiEventCode code, const TPoint& aPos)
       
   731 {
       
   732 
       
   733     m_previousUiGenerated = code ;
       
   734     return CUiEvent::NewL(code, m_gestureStartXY, aPos, getPreviousXY(aPos),
       
   735         isTimerMessage(), m_hwe.iTarget, getInterval(), m_index, m_hwe.iTime.Int64()) ;
       
   736 }
       
   737 /*!
       
   738  * Return the previous XY position and store the current for next round
       
   739  */
       
   740 TPoint CStateEngine::getPreviousXY(const TPoint& aCurrentXY)
       
   741 {
       
   742     TPoint p = m_previousXY ;
       
   743     m_previousXY = aCurrentXY ;
       
   744     return p ;
       
   745 }
       
   746 /*!
       
   747  * \return true, if the current event was timer triggered
       
   748  */
       
   749 bool CStateEngine::isTimerMessage()
       
   750 {
       
   751     return (m_hwe.iType >= ETouchTimer); // NOTE: if new events are added at the end of the list this needs to be changed
       
   752 }
       
   753 /*!
       
   754  * Action method.
       
   755  * Generate the Touch UI event.
       
   756  * If there are a set of touch points collected, calculate the position to the
       
   757  * Touch UI event to be the average of the collected points.
       
   758  */
       
   759 void CStateEngine::ProduceTouch()
       
   760 {
       
   761     m_wasFiltered = false ;
       
   762     CUiEvent* cue = NULL;
       
   763     getInterval() ; // dummy call to initialize the variable....
       
   764     TInt err(KErrNone);
       
   765     if (iTouchPoints.Count()>0)
       
   766     {
       
   767         // calculate average of the touch points
       
   768         m_currentTouchXY = calculateTouchAverageFromPoints() ;
       
   769         TRAP(err, cue = createUIEventL(stmUiEventEngine::ETouch, m_currentTouchXY)) ;
       
   770     }
       
   771     else
       
   772     {
       
   773         TRAP(err, cue = createUIEventL(stmUiEventEngine::ETouch, m_uiEventXY)) ;
       
   774     }
       
   775     if(!err)
       
   776         m_config->m_uiEventSender->AddEvent(cue) ;
       
   777 }
       
   778 /*!
       
   779  * Action method
       
   780  * Generate the Move UI event.  The position of the event has been set in the SetCurrentPos
       
   781  * The previous position needs some special handling, if filtering has been used.
       
   782  * \sa SetCurrentPos
       
   783  */
       
   784 void CStateEngine::ProduceMove()
       
   785 {
       
   786     m_wasFiltered = false ;
       
   787     if (m_uiEventXY == m_previousXY) {
       
   788     return;
       
   789     }
       
   790     CUiEvent* cue = NULL;
       
   791     TRAPD(err, cue = createUIEventL(stmUiEventEngine::EMove, m_uiEventXY)) ;
       
   792 
       
   793     if(!err)
       
   794     	m_config->m_uiEventSender->AddEvent(cue) ;
       
   795 }
       
   796 /*!
       
   797  * Action method
       
   798  * Generate the Release UI event.
       
   799  */
       
   800 void CStateEngine::ProduceRelease()
       
   801 {
       
   802     m_wasFiltered = false ;
       
   803     CUiEvent* cue = NULL;
       
   804     TRAPD(err, cue = createUIEventL(stmUiEventEngine::ERelease, m_uiEventXY)) ;
       
   805     if(!err)
       
   806     	m_config->m_uiEventSender->AddEvent(cue) ;
       
   807 
       
   808     if (m_config->m_enableLogging)
       
   809     {
       
   810         LOGFLUSH ;
       
   811     }
       
   812 }
       
   813 /*!
       
   814  * Action method
       
   815  * Generate the Hold UI event.
       
   816  */
       
   817 void CStateEngine::ProduceHold()
       
   818 {
       
   819     m_wasFiltered = false ;
       
   820     CUiEvent* cue = NULL;
       
   821     TRAPD(err, cue = createUIEventL(stmUiEventEngine::EHold, m_holdCentre)) ;
       
   822     if(!err)
       
   823  	   m_config->m_uiEventSender->AddEvent(cue) ;
       
   824 
       
   825 }
       
   826 /*!
       
   827  * Action method
       
   828  * Rename the current event to drag.  This is used when the accidental up/down message pair
       
   829  * has been detected, the DOWN event is handled as it were a move event.
       
   830  */
       
   831 void CStateEngine::RenameToDrag()
       
   832 {
       
   833     m_hwe.iType = stmUiEventEngine::EDrag ;
       
   834 }
       
   835 /*!
       
   836  * Action method
       
   837  * Initialize the touch time area.  Clear the array for collected touch points and
       
   838  * calculate the touch rectangle.
       
   839  */
       
   840 void CStateEngine::PrepareTouchTimeArea()
       
   841 {
       
   842     if (iTouchPoints.Count()>0) iTouchPoints.Reset() ;
       
   843     m_touchRect = ToleranceRect(m_hwe.iPosition, m_config->m_touchTimeTolerance) ;
       
   844 }
       
   845 
       
   846 /*!
       
   847  * Action method
       
   848  * Initialize the touch area.  Clear the array for collected touch points and
       
   849  * calculate the touch rectangle.
       
   850  */
       
   851 void CStateEngine::PrepareTouchArea()
       
   852 {
       
   853     if (iTouchPoints.Count()>0) iTouchPoints.Reset() ;
       
   854     m_touchRect = ToleranceRect(m_hwe.iPosition, m_config->m_touchTolerance) ;
       
   855 }
       
   856 /*!
       
   857  * Action method
       
   858  * Initialize the hold area rectangle.
       
   859  */
       
   860 void CStateEngine::PrepareHoldArea()
       
   861 {
       
   862     m_holdRect = ToleranceRect(m_hwe.iPosition, m_config->m_holdTolerance) ;
       
   863 }
       
   864 /*!
       
   865  * Action method
       
   866  * Store the current position and time always when we see EDrag.  The stored value is used
       
   867  * to calculate correct speed after filtered messages.
       
   868  */
       
   869 void CStateEngine::StoreMovePos()
       
   870 {
       
   871     if (m_config->m_enableLogging)
       
   872     {
       
   873         LOGARG("store move pos from (%d, %d) to (%d, %d)",
       
   874                 m_lastFilteredPosition.iX, m_lastFilteredPosition.iY,m_hwe.iPosition.iX,
       
   875                 m_hwe.iPosition.iY  ) ;
       
   876     }
       
   877     m_lastFilteredPosition = m_hwe.iPosition ;
       
   878     m_lastFilteredMessageTime = m_hwe.iTime ;
       
   879 
       
   880 }
       
   881 /*!
       
   882  * Action method
       
   883  * Store the current position and time.
       
   884  */
       
   885 void CStateEngine::SetCurrentPos()
       
   886 {
       
   887     m_uiEventXY = m_hwe.iPosition ;
       
   888 }
       
   889 /*!
       
   890  * Action method
       
   891  * Initialize the gesture starting.
       
   892  */
       
   893 void CStateEngine::SetGestureStart()
       
   894 {
       
   895     m_gestureStartXY = m_hwe.iPosition ;
       
   896     m_previousXY = m_hwe.iPosition ;
       
   897     m_gestureTarget = m_hwe.iTarget ;
       
   898     iDragPoints.ResetAndDestroy() ;
       
   899     iTouchPoints.Reset() ;
       
   900 }
       
   901 /*!
       
   902  * Action method
       
   903  * Add current point to the set of touch points.
       
   904  */
       
   905 void CStateEngine::AddToTouch()
       
   906 {
       
   907     iTouchPoints.Append(THwEvent(m_hwe.iType,
       
   908                                  m_hwe.iPosition,
       
   909                                  m_hwe.iTime,
       
   910                                  m_hwe.iTarget,
       
   911                                  m_index)
       
   912                         ) ;
       
   913     // calculate the average of touch points and move the touch area accordingly
       
   914     // this allows slight movement of the figertip while inside touch time
       
   915     if (iTouchPoints.Count()>2)
       
   916     {
       
   917         TPoint newtp = calculateTouchAverageFromPoints() ;
       
   918         m_touchRect = ToleranceRect(newtp, m_config->m_touchTolerance) ;
       
   919         m_holdRect = ToleranceRect(newtp, m_config->m_holdTolerance) ;
       
   920     }
       
   921 }
       
   922 /*!
       
   923  * Action method
       
   924  * Add the current point to the set of dragging points.
       
   925  * The set of dragging points is examined to determine if a enw hold has been started.
       
   926  */
       
   927 void CStateEngine::AddDraggingPos()
       
   928 {
       
   929     iDragPoints.Append(new THwEvent(m_hwe.iType,
       
   930                                     m_hwe.iPosition,
       
   931                                     m_hwe.iTime,
       
   932                                     m_hwe.iTarget,
       
   933                                     m_index)
       
   934                        ) ;
       
   935 }
       
   936 /*!
       
   937  * HandleStateEvent processes one event, which can be either pointer event or timer event.
       
   938  * The event is handled by calling the turnStateMachine method.
       
   939  */
       
   940 bool CStateEngine::handleStateEvent()
       
   941 {
       
   942     // We get an event into m_hwe by this moment, lets kick the state machine
       
   943     m_wasFiltered = ETrue ;
       
   944 
       
   945     CalculateDelta() ;
       
   946     turnStateMachine() ;
       
   947 
       
   948     m_previousPointerEventPosition = m_hwe.iPosition ;
       
   949     return m_wasFiltered ;
       
   950 }
       
   951 
       
   952 /*!
       
   953  *  Get the current touch rectangle.  If touch state not currently on, returns TRect(TPoint(0,0),TPoint(0,0))
       
   954  * (touch state meaning that the touch timer is still running and the points have been kept inside the area)
       
   955  */
       
   956 TRect CStateEngine::getTouchArea()
       
   957 {
       
   958     return m_touchRect ;
       
   959 }
       
   960 /*!
       
   961  * get the hold area rectangle
       
   962  */
       
   963 TRect CStateEngine::getHoldArea()
       
   964 {
       
   965     return m_holdRect ;
       
   966 }
       
   967 /*!
       
   968  * MStateMachine method.
       
   969  */
       
   970 bool CStateEngine::wasLastMessageFiltered()
       
   971 {
       
   972     return m_wasFiltered ;
       
   973 }
       
   974 
       
   975 /*!
       
   976  * Check if the last X points in the stored points look like the movement has stopped
       
   977  */
       
   978 bool CStateEngine::isNewHoldingPoint()
       
   979 {
       
   980     int x = iDragPoints.Count();
       
   981     if (x > 2)    // are there any points to be checked?
       
   982     {
       
   983         THwEvent* phwe = iDragPoints[x-1] ;
       
   984         THwEvent* phweinsidehold = phwe ;
       
   985         TRect recth = ToleranceRect(phwe->iPosition, m_config->m_holdTolerance) ;
       
   986         // Look backwards from the last point to see if there are enought points (enough in time) to look like a hold
       
   987         x -= 2 ;
       
   988         while (x > 0 && recth.Contains(iDragPoints[x]->iPosition))
       
   989         {
       
   990             phweinsidehold = iDragPoints[x];
       
   991             --x;
       
   992         }
       
   993         TTimeIntervalMicroSeconds tival = phwe->iTime.MicroSecondsFrom(phweinsidehold->iTime) ;
       
   994 
       
   995         /**
       
   996          * remove the extra points from the list if they are outside of holding area
       
   997          */
       
   998         while (x > 0)
       
   999         {
       
  1000             THwEvent* p = iDragPoints[x] ;
       
  1001             delete p ;
       
  1002             iDragPoints.Remove(x) ;
       
  1003             --x ;
       
  1004         }
       
  1005 
       
  1006         // See the time difference of the two points which still are inside the hold area
       
  1007         TTimeIntervalMicroSeconds limit = m_config->m_holdTimerLimit/2 ;
       
  1008         if (tival > limit)
       
  1009         {
       
  1010             if (m_config->m_enableLogging)
       
  1011             {
       
  1012                 LOGARG("isNewHoldingPoint: %s, dragpoints count %d",
       
  1013                         stateNames[m_currentState], iDragPoints.Count()) ;
       
  1014             }
       
  1015             return true ;
       
  1016         }
       
  1017     }
       
  1018     else
       
  1019     {
       
  1020         // one or 0 points does not look like hold
       
  1021 
       
  1022     }
       
  1023     return false ;
       
  1024 }
       
  1025 /*!
       
  1026  * calculate simple average of the touch points, i.e. calculate the average of the previous and current
       
  1027  * position.  Note that the touch point remains the same, this just calculates new value for the UI position
       
  1028  */
       
  1029 void CStateEngine::CalculateTouchAverage()
       
  1030 {
       
  1031     m_uiEventXY.iX =  (m_uiEventXY.iX+m_hwe.iPosition.iX)/2 ;
       
  1032     m_uiEventXY.iY =  (m_uiEventXY.iY+m_hwe.iPosition.iY)/2 ;
       
  1033 }
       
  1034 /*!
       
  1035  * Calculate the movement vector.
       
  1036  */
       
  1037 void CStateEngine::CalculateDelta()
       
  1038 {
       
  1039     m_deltaVector.iX = m_hwe.iPosition.iX-m_previousPointerEventPosition.iX ;
       
  1040     m_deltaVector.iY = m_hwe.iPosition.iY-m_previousPointerEventPosition.iY ;
       
  1041 }
       
  1042 /*!internal
       
  1043  * Debug logging method
       
  1044  */
       
  1045 void CStateEngine::DebugPrintState(TStateMachineState anextstate)
       
  1046 {
       
  1047     if (m_config->m_enableLogging)
       
  1048     {
       
  1049     LOGARG("%s: cuiev(%d,%d) cTxy ((%d,%d)(%d,%d)) cHxy ((%d,%d)(%d,%d)) gsXY(%d,%d) dV(%d,%d) EVNT(%d,%d (%s)) going to %s",
       
  1050             stateNames[m_currentState],
       
  1051             m_uiEventXY.iX, m_uiEventXY.iY,
       
  1052             m_touchRect.iTl.iX, m_touchRect.iTl.iY,m_touchRect.iBr.iX, m_touchRect.iBr.iY,
       
  1053             m_holdRect.iTl.iX, m_holdRect.iTl.iY,m_holdRect.iBr.iX, m_holdRect.iBr.iY,
       
  1054             m_gestureStartXY.iX, m_gestureStartXY.iY,
       
  1055             m_deltaVector.iX, m_deltaVector.iY,
       
  1056             m_hwe.iPosition.iX, m_hwe.iPosition.iY, hweventNames[m_hwe.iType],
       
  1057             stateNames[anextstate]
       
  1058             );
       
  1059     }
       
  1060 }
       
  1061 
       
  1062 /*!
       
  1063  * calculate the rectangle for touch or hold
       
  1064  */
       
  1065 TRect CStateEngine::ToleranceRect(const TPoint& aCenterPoint, const TPoint& tolerance)
       
  1066 {
       
  1067     // grow by the tolerance length, while keeping the center point
       
  1068     TRect toleranceRect(
       
  1069         aCenterPoint - tolerance,
       
  1070         aCenterPoint + tolerance);
       
  1071     return toleranceRect;
       
  1072 }
       
  1073 /*!
       
  1074  * turnStateMachine.  Go trough the state elements found for the current event
       
  1075  * until the event has been consumed.
       
  1076  *
       
  1077  * \pre m_currentState defines the current state and the index to the allStates array.
       
  1078  * \pre m_hwe is the message being handled.  The corresponding STATE_ELEMENT array must be found and processed.
       
  1079  *
       
  1080  */
       
  1081 void CStateEngine::turnStateMachine()
       
  1082 {
       
  1083 
       
  1084     const STATE_ELEMENT* pelement ;
       
  1085     m_eventConsumed = false ;   // run the loop until the event has been consumed
       
  1086     // Now run trough the motions of the state elements, and prepare to change to next state while doing so.
       
  1087     // If the state elements set the m_eventConsumed then all is done
       
  1088     while (!m_eventConsumed)
       
  1089     {
       
  1090         int i = 0 ;
       
  1091         const STATE* const pcurrentstate = allStates[m_currentState] ;
       
  1092         // Since each state definition must contain entries for all possible events the following loop cannot fail ;-)
       
  1093         while (pcurrentstate[i].theEvent != m_hwe.iType ) ++i ;
       
  1094         pelement = pcurrentstate[i].stateElements ;
       
  1095         TStateMachineState      nextState = Eignore ;
       
  1096         /*
       
  1097          * Handle the individual state elements.  If there is a condition function,
       
  1098          * call the function and if it returns true, handle the action function and possible next state
       
  1099          * if the condition returns false, continue to next element
       
  1100          * if there is no condition, run the action function if it exists.
       
  1101          * if the next state is defined (i.e it is != Eignore), go to that state
       
  1102          */
       
  1103         while (nextState == Eignore)
       
  1104         {
       
  1105             condition_t cndfunc = pelement->conditionFunction ;
       
  1106             action_t    actfunc = pelement->actionFunction ;
       
  1107             if (cndfunc != 0)
       
  1108             {
       
  1109                 /*
       
  1110                  * There was a condition function, call it to see whether the action needs to performed and/or the next satte defined
       
  1111                  */
       
  1112                 if (cndfunc(this))
       
  1113                 {
       
  1114                     // Condition was true, handle it
       
  1115                     // call the action if it exists
       
  1116                     if (actfunc != 0) actfunc(this) ;
       
  1117                     // and now get to the next state
       
  1118                     nextState   = pelement->nextState ; // Note that while this remains Eignore there are elements to be run
       
  1119                 }
       
  1120             }
       
  1121             else
       
  1122             {
       
  1123                 /**
       
  1124                  * No condition function, call the possible action function and get the next state
       
  1125                  */
       
  1126                 if (actfunc != 0) actfunc(this) ;
       
  1127                 nextState   = pelement->nextState ;     // Note that while this remains Eignore there are elements to be run
       
  1128             }
       
  1129             ++pelement ;    // next entry in the elements
       
  1130         }
       
  1131         if (m_config->m_enableLogging) DebugPrintState(nextState) ;
       
  1132         m_currentState = nextState ;    // Change to the next state
       
  1133     }
       
  1134 }
       
  1135 TTimeIntervalMicroSeconds CStateEngine::getInterval()
       
  1136 {
       
  1137     TTime now ;
       
  1138     now.HomeTime() ;
       
  1139     TTimeIntervalMicroSeconds interval = now.MicroSecondsFrom(m_lastMessageTime) ;
       
  1140     m_lastMessageTime = now ;
       
  1141     return interval ;
       
  1142 }
       
  1143 
       
  1144 TPoint CStateEngine::calculateTouchAverageFromPoints()
       
  1145 {
       
  1146     TPoint tp ;
       
  1147     int count = iTouchPoints.Count() ;
       
  1148     for (int i = 0; i < count; i++)
       
  1149     {
       
  1150         tp += iTouchPoints[i].iPosition;
       
  1151     }
       
  1152     if(count)
       
  1153         {
       
  1154         tp.iX /= count ;
       
  1155         tp.iY /= count ;
       
  1156         }
       
  1157     return tp ;
       
  1158 }
       
  1159