License conversion from Flora to Apache 2.0
[platform/core/uifw/dali-core.git] / dali / integration-api / events / touch-event-combiner.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/integration-api/events/touch-event-combiner.h>
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23
24 // INTERNAL INCLUDES
25 #include <dali/integration-api/events/touch-event-integ.h>
26 #include <dali/public-api/common/dali-common.h>
27
28 namespace Dali
29 {
30
31 namespace Integration
32 {
33
34 namespace
35 {
36 const unsigned long DEFAULT_MINIMUM_MOTION_TIME( 1u );
37 const Vector2 DEFAULT_MINIMUM_MOTION_DISTANCE( 1.0f, 1.0f );
38 } // unnamed namespace
39
40 struct TouchEventCombiner::PointInfo
41 {
42   // Construction
43
44   /**
45    * Constructor
46    * @param[in]  touchPoint  The point to add.
47    * @param[in]  pointTime   The time of the point event.
48    */
49   PointInfo( const TouchPoint& touchPoint, unsigned long pointTime )
50   : point( touchPoint ),
51     time( pointTime )
52   {
53   }
54
55   // Data
56
57   TouchPoint point;   ///< The point.
58   unsigned long time; ///< The time the point event took place.
59 };
60
61 TouchEventCombiner::TouchEventCombiner()
62 : mMinMotionTime( DEFAULT_MINIMUM_MOTION_TIME ),
63   mMinMotionDistance( DEFAULT_MINIMUM_MOTION_DISTANCE )
64 {
65 }
66
67 TouchEventCombiner::TouchEventCombiner( unsigned long minMotionTime, float minMotionXDistance, float minMotionYDistance )
68 : mMinMotionTime( minMotionTime ),
69   mMinMotionDistance( minMotionXDistance, minMotionYDistance )
70 {
71   DALI_ASSERT_ALWAYS( minMotionXDistance >= 0.0f && minMotionYDistance >= 0.0f && "Negative values not allowed\n" );
72 }
73
74 TouchEventCombiner::TouchEventCombiner( unsigned long minMotionTime, Vector2 minMotionDistance )
75 : mMinMotionTime( minMotionTime ),
76   mMinMotionDistance( minMotionDistance )
77 {
78   DALI_ASSERT_ALWAYS( minMotionDistance.x >= 0.0f && minMotionDistance.y >= 0.0f && "Negative values not allowed\n" );
79 }
80
81 TouchEventCombiner::~TouchEventCombiner()
82 {
83 }
84
85 bool TouchEventCombiner::GetNextTouchEvent( const TouchPoint& point, unsigned long time, TouchEvent& touchEvent )
86 {
87   bool dispatchEvent( false );
88
89   switch ( point.state )
90   {
91     case TouchPoint::Down:
92     {
93       touchEvent.time = time;
94       bool addToContainer( true );
95
96       // Iterate through already stored touch points and add to TouchEvent
97       for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
98       {
99         if ( iter->point.deviceId != point.deviceId )
100         {
101           iter->point.state = TouchPoint::Stationary;
102         }
103         else
104         {
105           // System has sent us two down points for the same point ID, update our stored data to latest.
106           // We do not want to emit another down event for this Point Device ID.
107
108           addToContainer = false;
109           iter->point = point;
110           iter->time = time;
111         }
112         touchEvent.AddPoint( iter->point );
113       }
114
115       // Add new touch point to the list and to the TouchEvent
116       if (addToContainer)
117       {
118         mPressedPoints.push_back( PointInfo( point, time ) );
119         touchEvent.AddPoint( point );
120         dispatchEvent = true; // Only dispatch event if just added to container
121       }
122
123       break;
124     }
125
126     case TouchPoint::Up:
127     {
128       touchEvent.time = time;
129
130       // Find pressed touch point in local list (while also adding the stored points to the touchEvent)
131       PointInfoContainer::iterator match( mPressedPoints.end() );
132       for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
133       {
134         if ( point.deviceId == iter->point.deviceId )
135         {
136           match = iter;
137
138           // Add new point to the TouchEvent
139           touchEvent.AddPoint( point );
140         }
141         else
142         {
143           iter->point.state = TouchPoint::Stationary;
144           touchEvent.AddPoint( iter->point );
145         }
146       }
147
148       if ( match != mPressedPoints.end() )
149       {
150         mPressedPoints.erase( match );
151         dispatchEvent = true; // We should only dispatch events if the point was actually pressed in this window
152       }
153       break;
154     }
155
156     case TouchPoint::Motion:
157     {
158       if ( !mPressedPoints.empty() )
159       {
160         touchEvent.time = time;
161
162         PointInfoContainer::iterator match = mPressedPoints.end();
163         for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
164         {
165           if ( point.deviceId == iter->point.deviceId )
166           {
167             unsigned long timeDiff( time - iter->time );
168
169             if ( timeDiff < mMinMotionTime )
170             {
171               // Motion event sent too soon after previous event so ignore
172               break;
173             }
174
175             if ( ( abs( point.screen.x - iter->point.screen.x ) < mMinMotionDistance.x ) &&
176                  ( abs( point.screen.y - iter->point.screen.y ) < mMinMotionDistance.y ) )
177             {
178               // Not enough positional change from last event so ignore
179               break;
180             }
181
182             match = iter;
183
184             // Add new touch point to the TouchEvent
185             touchEvent.AddPoint( point );
186           }
187           else
188           {
189             iter->point.state = TouchPoint::Stationary;
190             touchEvent.AddPoint( iter->point );
191           }
192         }
193
194         if ( match != mPressedPoints.end() )
195         {
196           PointInfo matchedPoint( point, time );
197           std::swap( *match, matchedPoint );
198
199           dispatchEvent = true;
200         }
201       }
202       break;
203     }
204
205     case TouchPoint::Interrupted:
206     {
207       Reset();
208
209       // We should still tell core about the interruption.
210       touchEvent.AddPoint( point );
211       dispatchEvent = true;
212       break;
213     }
214
215     default:
216       break;
217   }
218
219   return dispatchEvent;
220 }
221
222 void TouchEventCombiner::SetMinimumMotionTimeThreshold( unsigned long minTime )
223 {
224   mMinMotionTime = minTime;
225 }
226
227 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( float minDistance )
228 {
229   DALI_ASSERT_ALWAYS( minDistance >= 0.0f && "Negative values not allowed\n" );
230
231   mMinMotionDistance.x = mMinMotionDistance.y = minDistance;
232 }
233
234 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( float minXDistance, float minYDistance )
235 {
236   DALI_ASSERT_ALWAYS( minXDistance >= 0.0f && minYDistance >= 0.0f && "Negative values not allowed\n" );
237
238   mMinMotionDistance.x = minXDistance;
239   mMinMotionDistance.y = minYDistance;
240 }
241
242 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( Vector2 minDistance )
243 {
244   DALI_ASSERT_ALWAYS( minDistance.x >= 0.0f && minDistance.y >= 0.0f && "Negative values not allowed\n" );
245
246   mMinMotionDistance = minDistance;
247 }
248
249 unsigned long TouchEventCombiner::GetMinimumMotionTimeThreshold() const
250 {
251   return mMinMotionTime;
252 }
253
254 Vector2 TouchEventCombiner::GetMinimumMotionDistanceThreshold() const
255 {
256   return mMinMotionDistance;
257 }
258
259 void TouchEventCombiner::Reset()
260 {
261   mPressedPoints.clear();
262 }
263
264 } // namespace Integration
265
266 } // namespace Dali