Add LCOV remarkers to increase line coverage rate
[platform/core/api/maps-service.git] / src / view / gesture_processor.cpp
1 /* Copyright (c) 2010-2014 Samsung Electronics Co., Ltd. All rights reserved.
2  *
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 #include "gesture_processor.h"
19 #include "commands.h"
20 #include <glib.h>
21 #include <math.h>  /* for sqrt */
22 #include "gesture_detector_statemachine.h"
23 #include "maps_view_plugin.h"
24
25
26 #define GESTURE_ZOOM_STATIC_FRICTION    0.15
27 #define GESTURE_ZOOM_MOVEMENT_UNIT              0.02
28
29 #define GESTURE_ROTATION_STATIC_FRICTION        15.0
30 #define GESTURE_ROTATION_MOVEMENT_UNIT          0.15
31
32 /*----------------------------------------------------------------------------*/
33
34
35 /* Using protected functions of Map View */
36 extern bool _maps_view_is_gesture_available(maps_view_h view, maps_view_gesture_e gesture);
37 extern maps_view_action_e _maps_view_get_gesture_action(maps_view_h view, maps_view_gesture_e gesture);
38 extern void *_maps_view_get_maps_service_ptr(maps_view_h view);
39 extern maps_view_object_h _maps_view_object_hit_test(maps_view_h view, int x, int y, maps_view_gesture_e gesture);
40 extern int _maps_view_event_data_set_gesture_type(maps_view_event_data_h event, maps_view_gesture_e gesture_type);
41 extern int _maps_view_event_data_set_position(maps_view_event_data_h event, int x, int y);
42 extern int _maps_view_event_data_set_coordinates(maps_view_event_data_h event, maps_coordinates_h coordinates);
43 extern int _maps_view_event_data_set_center(maps_view_event_data_h event, maps_coordinates_h center);
44 extern int _maps_view_event_data_set_fingers(maps_view_event_data_h event, int fingers);
45 extern int _maps_view_event_data_set_zoom_factor(maps_view_event_data_h event, double zoom_factor);
46 extern int _maps_view_event_data_set_rotation_angle(maps_view_event_data_h event, double rotation_angle);
47 extern maps_view_event_data_h _maps_view_create_event_data(maps_view_event_type_e type);
48 extern void _maps_view_invoke_event_callback(maps_view_h view, maps_view_event_data_h event_data);
49
50 #ifdef _MOVE_CENTER_COMMAND_DEFINED_
51 extern int _maps_view_set_center_directly(maps_view_h view, maps_coordinates_h coordinates);
52 extern int _maps_view_get_plugin_center(const maps_view_h view, maps_coordinates_h *center);
53 #endif /* _MOVE_CENTER_COMMAND_DEFINED_ */
54
55 /* ---------------------------------------------------------------------------*/
56
57 //LCOV_EXCL_START
58 view::zoom_calculator::zoom_calculator(const touch_point &start_tp_f1,
59                                         const touch_point &cur_tp_f1,
60                                         const touch_point &start_tp_f2,
61                                         const touch_point &cur_tp_f2)
62         : _start_tp_f1(start_tp_f1)
63         , _cur_tp_f1(cur_tp_f1)
64         , _start_tp_f2(start_tp_f2)
65         , _cur_tp_f2(cur_tp_f2)
66         , _new_zoom_factor(.0)
67         , _new_rotation_angle(.0)
68         , _zoom_happend(false)
69         , _rotation_happend(false)
70 {
71         /* Finding the start radius */
72         const int start_dx = _start_tp_f2._x - _start_tp_f1._x;
73         const int start_dy = _start_tp_f2._y - _start_tp_f1._y;
74         const double start_r = sqrt(start_dx * start_dx + start_dy * start_dy) / 2;
75
76         /* Finding the cur radius */
77         const int cur_dx = _cur_tp_f2._x - _cur_tp_f1._x;
78         const int cur_dy = _cur_tp_f2._y - _cur_tp_f1._y;
79         const double cur_r = sqrt(cur_dx * cur_dx + cur_dy * cur_dy) / 2;
80
81         /* Calculating the zoom factor */
82         if ((start_r != .0) && (cur_r != .0) && (start_r != cur_r)) {
83                 int dpi;
84                 maps_get_screen_dpi(&dpi);
85
86                 /* basically change 1 zoom level per inch */
87                 /* additional adjust depending on the size of gesture which a user operates */
88                 if (cur_r > start_r) {
89                         _new_zoom_factor = ((cur_r - start_r) / dpi)
90                                                          + ((cur_r / start_r) - 1) * 0.05;
91                 } else {
92                         _new_zoom_factor = -((start_r - cur_r) / dpi)
93                                                          + -((start_r / cur_r) - 1) * 0.05;
94                 }
95                 _zoom_happend = true;
96         }
97
98         /* Calculating the rotation angle */
99         const double inner1 = sqrt(start_dx * start_dx + start_dy * start_dy);
100         const double inner2 = sqrt(cur_dx * cur_dx + cur_dy * cur_dy);
101         const double norm = inner1 * inner2;
102         const double inner = (cur_dx * start_dx) + (cur_dy * start_dy);
103         const double angle = acos(inner / norm);
104         const double curl = (cur_dx * start_dy) - (cur_dy * start_dx);
105
106         if (curl < 0) {
107                 _new_rotation_angle = (angle / M_PI * 180.);
108                 _rotation_happend = true;
109         } else if (curl > 0) {
110                 _new_rotation_angle = -(angle / M_PI * 180.);
111                 _rotation_happend = true;
112         }
113 }
114
115
116 /* ---------------------------------------------------------------------------*/
117
118
119 view::gesture_processor::gesture_processor(gesture_detector *gd)
120         : _gd(gd)
121 {
122 }
123
124 view::gesture_processor::~gesture_processor()
125 {
126 }
127
128 extern session::command_queue *__maps_view_select_q();
129
130 session::command_queue *view::gesture_processor::q()
131 {
132         /* This is a thread-friendly mode, when all heavy computations
133          *  are performed in the "idle" mode.
134          *  This mode is good for apps, which has other active widgets in
135          *  addition to Map View.
136          */
137         /* return session::command_queue_view::interface(); */
138
139
140         /* This is a draw-enforcing mode, when the rendering and drawing
141          *  of the map is happening as fast as it possible.
142          *  This mode is good when the Map View is a single active widget
143          *  of the App */
144         /*return session::command_queue_sync::interface();*/
145
146         /* Will use the same queue as used in Map View */
147         return __maps_view_select_q();
148 }
149
150 void *view::gesture_processor::get_maps()
151 {
152         return _maps_view_get_maps_service_ptr(_gd->_view);
153 }
154
155 session::command *view::gesture_processor::construct_gesture_command(
156                                                 maps_view_gesture_e gesture,
157                                                 const maps_coordinates_h c,
158                                                 const bool zoom_changed,
159                                                 const double zoom,
160                                                 const bool rotation_changed,
161                                                 const double angle)
162 {
163         double zoom_factor = zoom;
164         double rotation_angle = angle;
165
166         /* Check if the gesture is available */
167         if (!_maps_view_is_gesture_available(_gd->_view, gesture)) {
168                 return session::command::empty_ptr();
169         }
170
171         /* Perform gesture action */
172         switch(_maps_view_get_gesture_action(_gd->_view, gesture)) {
173         case MAPS_VIEW_ACTION_SCROLL: {
174                 maps_coordinates_h coords = c;
175                 maps_coordinates_h center_clone = NULL;
176                 if (!coords) {
177                         maps_view_get_center(_gd->_view, &center_clone);
178                         coords = center_clone;
179                 }
180                 session::command *cmd =
181                         new session::command_view_set_center(get_maps(), _gd->_view, coords);
182                 maps_coordinates_destroy(center_clone);
183                 return cmd;
184         }
185         case MAPS_VIEW_ACTION_ZOOM_IN: {
186                 MAPS_LOGD("MAPS_VIEW_ACTION_ZOOM_IN");
187                 if (zoom_factor == .0 || !zoom_changed)
188                         maps_view_get_zoom_factor(_gd->_view, &zoom_factor);
189                 return new session::command_view_zoom(get_maps(), _gd->_view, zoom_factor + 1.);
190         }
191         case MAPS_VIEW_ACTION_ZOOM_OUT: {
192                 MAPS_LOGD("MAPS_VIEW_ACTION_ZOOM_OUT");
193                 if (zoom_factor == .0 || !zoom_changed)
194                         maps_view_get_zoom_factor(_gd->_view, &zoom_factor);
195                 return new session::command_view_zoom(get_maps(), _gd->_view, zoom_factor - 1.);
196         }
197         case MAPS_VIEW_ACTION_ZOOM_AND_SCROLL: {
198                 MAPS_LOGD("MAPS_VIEW_ACTION_ZOOM_AND_SCROLL");
199                 double cur_zoom_factor;
200                 maps_view_get_zoom_factor(_gd->_view, &cur_zoom_factor);
201
202                 if (zoom_factor == .0 || !zoom_changed)
203                         maps_view_get_zoom_factor(_gd->_view, &zoom_factor);
204
205                 if (zoom_factor == cur_zoom_factor) zoom_factor++;
206                 maps_view_set_zoom_factor(_gd->_view, zoom_factor);
207
208                 maps_coordinates_h coords = c;
209                 maps_coordinates_h center_clone = NULL;
210                 if (!coords) {
211                         maps_view_get_center(_gd->_view, &center_clone);
212                         coords = center_clone;
213                 }
214                 session::command *cmd =
215                         new session::command_view_set_center(get_maps(), _gd->_view, coords);
216                 maps_coordinates_destroy(center_clone);
217                 return cmd;
218         }
219         case MAPS_VIEW_ACTION_ZOOM:
220         case MAPS_VIEW_ACTION_ROTATE: {
221                 if (zoom_changed & rotation_changed) {
222                         MAPS_LOGD("rotation_angle=%f", rotation_angle);
223                         if (zoom_factor == .0)
224                                 maps_view_get_zoom_factor(_gd->_view, &zoom_factor);
225                         if (rotation_angle == .0)
226                                 maps_view_get_orientation(_gd->_view, &rotation_angle);
227                         rotation_angle -= (int(rotation_angle) / 360) * 360;
228                         return new session::command_view_zoom_rotate(get_maps(), _gd->_view, zoom_factor, rotation_angle);
229                 } else if (zoom_changed) {
230                         if (zoom_factor == .0)
231                                 maps_view_get_zoom_factor(_gd->_view, &zoom_factor);
232                         MAPS_LOGI("\t set new zoom command: %f\n", zoom_factor);
233                         return new session::command_view_zoom(get_maps(), _gd->_view, zoom_factor);
234                 } else if (rotation_changed) {
235                         if (rotation_angle == .0)
236                                 maps_view_get_orientation(_gd->_view, &rotation_angle);
237                         rotation_angle -= (int(rotation_angle) / 360) * 360;
238                         return new session::command_view_rotate(get_maps(), _gd->_view, rotation_angle);
239                 }
240                 break;
241         }
242         case MAPS_VIEW_ACTION_NONE:
243                 MAPS_LOGI("GESTURE: This Gesture is assigned with no Action");
244                 break;
245         default:
246                 break;
247         }
248         return session::command::empty_ptr();
249 }
250
251 void view::gesture_processor::on_long_press()
252 {
253         /* Assumed that we can tap only with a single finger */
254         touch_point tp = _gd->_info._finger_move[0];
255
256         /* Check if any object was affected */
257         maps_view_object_h hit = _maps_view_object_hit_test(_gd->_view,
258                                                 tp._x,
259                                                 tp._y,
260                                                 MAPS_VIEW_GESTURE_LONG_PRESS);
261         if (hit) return;
262
263         /* Enqueue the detected command */
264         maps_coordinates_h c = NULL;
265         maps_view_screen_to_geolocation(_gd->_view, tp._x, tp._y, &c);
266         q()->push(construct_gesture_command(MAPS_VIEW_GESTURE_LONG_PRESS, c,
267                                             false, .0, false, .0));
268
269         /* Invoke user registered event callback */
270         maps_view_event_data_h ed = _maps_view_create_event_data(MAPS_VIEW_EVENT_GESTURE);
271         if (ed) {
272                 _maps_view_event_data_set_gesture_type(ed, MAPS_VIEW_GESTURE_LONG_PRESS);
273                 _maps_view_event_data_set_position(ed, tp._x, tp._y);
274                 _maps_view_event_data_set_coordinates(ed, c);
275                 _maps_view_event_data_set_fingers(ed, 1);
276                 _maps_view_invoke_event_callback(_gd->_view, ed);
277                 maps_view_event_data_destroy(ed);
278         }
279         maps_coordinates_destroy(c);
280 }
281
282 void view::gesture_processor::on_double_tap()
283 {
284         /* Assumed that we can tap only with a single finger */
285         touch_point tp = _gd->_info_history._finger_up[0];
286
287         /* Check if any object was affected */
288         maps_view_object_h hit = _maps_view_object_hit_test(_gd->_view,
289                                                 tp._x,
290                                                 tp._y,
291                                                 MAPS_VIEW_GESTURE_DOUBLE_TAP);
292         if (hit) return;
293
294         /* Enqueue the detected command */
295         maps_coordinates_h c = NULL;
296         maps_view_screen_to_geolocation(_gd->_view, tp._x, tp._y, &c);
297         q()->push(construct_gesture_command(MAPS_VIEW_GESTURE_DOUBLE_TAP, c,
298                                             false, .0, false, .0));
299
300         /* Invoke user registered event callback */
301         maps_view_event_data_h ed = _maps_view_create_event_data(MAPS_VIEW_EVENT_GESTURE);
302         if (ed) {
303                 _maps_view_event_data_set_gesture_type(ed, MAPS_VIEW_GESTURE_DOUBLE_TAP);
304                 _maps_view_event_data_set_position(ed, tp._x, tp._y);
305                 _maps_view_event_data_set_coordinates(ed, c);
306                 _maps_view_event_data_set_fingers(ed, 1);
307                 _maps_view_invoke_event_callback(_gd->_view, ed);
308                 maps_view_event_data_destroy(ed);
309         }
310         maps_coordinates_destroy(c);
311 }
312
313 void view::gesture_processor::on_tap()
314 {
315         /* Assumed that we can tap only with a single finger */
316         touch_point tp = _gd->_info_history._finger_up[0];
317
318         /* Check if any object was affected */
319         maps_view_object_h hit = _maps_view_object_hit_test(_gd->_view,
320                                                 tp._x,
321                                                 tp._y,
322                                                 MAPS_VIEW_GESTURE_TAP);
323         if (hit) return;
324
325         /* Enqueue the detected command */
326         maps_coordinates_h c = NULL;
327         maps_view_screen_to_geolocation(_gd->_view, tp._x, tp._y, &c);
328         q()->push(construct_gesture_command(MAPS_VIEW_GESTURE_TAP, c,
329                                             false, .0, false, .0));
330
331         /* Invoke user registered event callback */
332         maps_view_event_data_h ed = _maps_view_create_event_data(MAPS_VIEW_EVENT_GESTURE);
333         if (ed) {
334                 _maps_view_event_data_set_gesture_type(ed, MAPS_VIEW_GESTURE_TAP);
335                 _maps_view_event_data_set_position(ed, tp._x, tp._y);
336                 _maps_view_event_data_set_coordinates(ed, c);
337                 _maps_view_event_data_set_fingers(ed, 1);
338                 _maps_view_invoke_event_callback(_gd->_view, ed);
339                 maps_view_event_data_destroy(ed);
340         }
341         maps_coordinates_destroy(c);
342 }
343
344 void view::gesture_processor::on_two_finger_tap()
345 {
346         MAPS_LOGW("\nON TWO FINGER TAP\n");
347
348         const touch_point tp_f1 = _gd->_info._finger_move[0];
349         const touch_point tp_f2 = _gd->_info._finger_move[1];
350         const touch_point gesture_center = calc_center(tp_f1, tp_f2);
351
352         /* Enqueue the detected command */
353         maps_coordinates_h c = NULL;
354         maps_view_screen_to_geolocation(_gd->_view, gesture_center._x,
355                                                 gesture_center._y, &c);
356
357         /* Enqueue the detected command */
358         q()->push(construct_gesture_command(MAPS_VIEW_GESTURE_2_FINGER_TAP, c,
359                                             false, .0, false, .0));
360
361         /* Invoke user registered event callback */
362         maps_view_event_data_h ed = _maps_view_create_event_data(MAPS_VIEW_EVENT_GESTURE);
363         if (ed) {
364                 _maps_view_event_data_set_gesture_type(ed, MAPS_VIEW_GESTURE_2_FINGER_TAP);
365                 _maps_view_event_data_set_position(ed, gesture_center._x, gesture_center._y);
366                 _maps_view_event_data_set_coordinates(ed, c);
367                 _maps_view_event_data_set_fingers(ed, 2);
368                 _maps_view_invoke_event_callback(_gd->_view, ed);
369                 maps_view_event_data_destroy(ed);
370         }
371         maps_coordinates_destroy(c);
372 }
373
374 void view::gesture_processor::on_panning_finished(int finger_no)
375 {
376         const touch_point cur_tp = _gd->_info._finger_move[finger_no];
377
378         /* Obtain fresh central coordinates of the map in the Plugin */
379         maps_coordinates_h c = NULL;
380         _maps_view_get_plugin_center(_gd->_view, &c);
381
382         /* Directly set the updated center of the map */
383         _maps_view_set_center_directly(_gd->_view, c);
384         maps_coordinates_destroy(c);
385         c = NULL;
386
387         maps_view_screen_to_geolocation(_gd->_view, cur_tp._x, cur_tp._y, &c);
388
389         /* Invoke user registered event callback */
390         maps_view_event_data_h ed = _maps_view_create_event_data(MAPS_VIEW_EVENT_GESTURE);
391         if(ed) {
392                 _maps_view_event_data_set_gesture_type(ed, MAPS_VIEW_GESTURE_SCROLL);
393                 _maps_view_event_data_set_position(ed, cur_tp._x, cur_tp._y);
394                 _maps_view_event_data_set_coordinates(ed, c);
395                 _maps_view_event_data_set_fingers(ed, 1);
396                 _maps_view_invoke_event_callback(_gd->_view, ed);
397                 maps_view_event_data_destroy(ed);
398         }
399         maps_coordinates_destroy(c);
400 }
401
402 void view::gesture_processor::on_pan(int finger_no)
403 {
404         /* Assumed that we can tap only with a single finger */
405         const touch_point cur_tp = _gd->_info._finger_move[finger_no];
406
407
408         /* Calculate the new center of the map by adding the delta
409         * to the original center (e.g. the center of the map before the event
410         * has started) */
411
412         /* Check if the gesture is available */
413         if (!_maps_view_is_gesture_available(_gd->_view, MAPS_VIEW_GESTURE_SCROLL))
414                 return;
415
416         touch_point prev_tp = _gd->_info._prev_finger_down[finger_no];
417         if (prev_tp.empty())
418                 prev_tp = _gd->_info._finger_down[finger_no];
419
420         /* a. Calculating the delta of the gesture */
421         int delta_x = cur_tp._x - prev_tp._x;
422         int delta_y = cur_tp._y - prev_tp._y;
423
424         /* b. Enque the command to move the center */
425         q()->push(new session::command_view_move_center(get_maps(),
426                                                         _gd->_view,
427                                                         -delta_x,
428                                                         -delta_y));
429
430         /* c. Get coordinates after delta_x and delta_y are updated */
431         maps_coordinates_h c = NULL;
432         maps_view_screen_to_geolocation(_gd->_view, cur_tp._x, cur_tp._y, &c);
433
434         /* Invoke user registered event callback */
435         maps_view_event_data_h ed = _maps_view_create_event_data(MAPS_VIEW_EVENT_GESTURE);
436         if (ed) {
437                 _maps_view_event_data_set_gesture_type(ed, MAPS_VIEW_GESTURE_SCROLL);
438                 _maps_view_event_data_set_position(ed, cur_tp._x, cur_tp._y);
439                 _maps_view_event_data_set_coordinates(ed, c);
440                 _maps_view_event_data_set_fingers(ed, 1);
441                 _maps_view_invoke_event_callback(_gd->_view, ed);
442                 maps_view_event_data_destroy(ed);
443         }
444         maps_coordinates_destroy(c);
445 }
446
447 view::touch_point view::gesture_processor::calc_center(
448                                                 const touch_point &tp1,
449                                                 const touch_point &tp2) const
450 {
451         const unsigned int timestamp = (tp2._timestamp > tp1._timestamp)
452                 ? tp2._timestamp
453                 : tp1._timestamp;
454         return touch_point(tp1._x + (tp2._x - tp1._x) / 2,
455                            tp1._y + (tp2._y - tp1._y) / 2,
456                            timestamp);
457 }
458
459 double view::gesture_processor::calc_angle(
460                                                 const double angle1,
461                                                 const double angle2) const
462 {
463         if (angle2 - angle1 <= 180)
464                 return (angle2 - angle1);
465         else
466                 return -(angle1 + (360 - angle2));
467 }
468
469 double view::gesture_processor::calc_angle(
470                                                 const touch_point tp1,
471                                                 const touch_point tp2) const
472 {
473         const int dx = tp2._x - tp1._x;
474         const int dy = tp2._y - tp1._y;
475 //      const double r = sqrt(dx * dx + dy * dy) / 2;
476         const double angle = atan2(dy, dx) / M_PI * 180. + 180.0;
477         return angle;
478 }
479
480 bool view::gesture_processor::on_zoom(bool zoom_changed, bool rotation_changed, double &zoom_factor)
481 {
482         /* Check if the gesture is available */
483         if (!_maps_view_is_gesture_available(_gd->_view, MAPS_VIEW_GESTURE_ZOOM))
484                 return false;
485
486         /* gesture_detector::log("view::gesture_processor::on_zoom", gesture_detector::FG_LITE_GREEN); */
487
488         /* First finger effective way by now */
489         const touch_point start_tp_f1 = _gd->_info._finger_down[0];
490         const touch_point cur_tp_f1 = _gd->_info._finger_move[0];
491
492         /* Second finger effective way by now */
493         const touch_point start_tp_f2 = _gd->_info._finger_down[1];
494         const touch_point cur_tp_f2 = _gd->_info._finger_move[1];
495
496         /* Calculating the current zoom factor, accordingly to effecitve ways of fingers */
497         zoom_calculator zc(start_tp_f1, cur_tp_f1, start_tp_f2, cur_tp_f2);
498         double new_zoom_factor = zc.get_zoom_factor();
499
500         /* Analyse zoom factor changes */
501         if (zc.zoom_happend()) {
502                 /* Apply newly calculated zoom factor */
503                 new_zoom_factor += _gd->_info._start_view_state._zoom_factor;
504
505                 /* Correct the zoom factor accordingly to allowed limits */
506                 /* TODO: it also may be cashed in the _info._start_view_state */
507                 int min_zoom_level = 0;
508                 int max_zoom_level = 0;
509                 maps_view_get_min_zoom_level(_gd->_view, &min_zoom_level);
510                 maps_view_get_max_zoom_level(_gd->_view, &max_zoom_level);
511                 new_zoom_factor = MIN(MAX(new_zoom_factor, min_zoom_level), max_zoom_level);
512
513                 /* Check if the zoom changed relatively to initial state */
514                 double diff = new_zoom_factor - _gd->_info._start_view_state._prev_zoom_factor;
515                 static double compensation = 0.;
516                 static double prev_diff = 0.;
517
518                 if (!zoom_changed && (fabs(diff) > GESTURE_ZOOM_STATIC_FRICTION * (1 + rotation_changed))) {
519                         compensation = diff * -1;
520                         zoom_changed = true;
521                 } else if (zoom_changed && (fabs(diff - prev_diff) > GESTURE_ZOOM_MOVEMENT_UNIT)) {
522                         zoom_changed = true;
523                 } else {
524                         zoom_changed = false;
525                 }
526
527                 if (zoom_changed) {
528                         new_zoom_factor += compensation;
529                         MAPS_LOGD("[zoom_changed] %f + %f -> %f",
530                                 _gd->_info._start_view_state._prev_zoom_factor, diff - prev_diff, new_zoom_factor);
531                         prev_diff = diff;
532                 }
533         }
534
535         if (!zoom_changed)
536                 return false;
537
538         zoom_factor = new_zoom_factor;
539         return true;
540 }
541
542 bool view::gesture_processor::on_rotate(bool zoom_changed, bool rotation_changed, double &rotation_angle)
543 {
544         /* Check if the gesture is available */
545         if (!_maps_view_is_gesture_available(_gd->_view, MAPS_VIEW_GESTURE_ROTATE))
546                 return false;
547
548         /* gesture_detector::log("view::gesture_processor::on_rotate", gesture_detector::FG_LITE_GREEN); */
549
550         /* First finger effective way by now */
551         const touch_point start_tp_f1 = _gd->_info._finger_down[0];
552         const touch_point cur_tp_f1 = _gd->_info._finger_move[0];
553
554         /* Second finger effective way by now */
555         const touch_point start_tp_f2 = _gd->_info._finger_down[1];
556         const touch_point cur_tp_f2 = _gd->_info._finger_move[1];
557
558         /* Calculating the current zoom factor, accordingly to effecitve ways of fingers */
559         zoom_calculator zc(start_tp_f1, cur_tp_f1, start_tp_f2, cur_tp_f2);
560         double new_rotation_angle = zc.get_rotation_angle();
561
562         /* Analyze rotation angle changes */
563         if (zc.rotation_happend()) {
564                 /* Apply newly calculated rotation angle */
565                 new_rotation_angle += _gd->_info._start_view_state._rotation_angle;
566
567                 /* Check if the zoom changed relatively to initial state */
568                 double diff = calc_angle(_gd->_info._start_view_state._prev_rotation_angle, new_rotation_angle);
569                 static double compensation = 0.;
570                 static double prev_diff = 0.;
571
572                 if (!rotation_changed && (fabs(diff) > GESTURE_ROTATION_STATIC_FRICTION * (1 + zoom_changed))) {
573                         compensation = diff * -1;
574                         rotation_changed = true;
575                 } else if (rotation_changed && (fabs(diff - prev_diff) > GESTURE_ROTATION_MOVEMENT_UNIT)) {
576                         rotation_changed = true;
577                 } else {
578                         rotation_changed = false;
579                 }
580
581                 if (rotation_changed) {
582                         new_rotation_angle = fmod(new_rotation_angle + compensation, 360.);
583                         MAPS_LOGD("[rotation_changed] %f + %f -> %f",
584                                 _gd->_info._start_view_state._prev_rotation_angle, diff - prev_diff, new_rotation_angle);
585                         prev_diff = diff;
586                 }
587         }
588
589         if (!rotation_changed)
590                 return false; // Seems nothing changed, we can return
591
592         rotation_angle = new_rotation_angle;
593         return true;
594 }
595
596 bool view::gesture_processor::on_two_fingers_pan(bool panning_changed, int *delta_x, int *delta_y)
597 {
598         /* Check if the gesture is available */
599         if (!_maps_view_is_gesture_available(_gd->_view, MAPS_VIEW_GESTURE_SCROLL))
600                 return false;
601
602         /* gesture_detector::log("view::gesture_processor::on_two_fingers_pan", gesture_detector::FG_LITE_GREEN); */
603
604         /* First finger effective way by now */
605         const touch_point start_tp_f1 = _gd->_info._finger_down[0];
606         const touch_point cur_tp_f1 = _gd->_info._finger_move[0];
607
608         /* Second finger effective way by now */
609         const touch_point start_tp_f2 = _gd->_info._finger_down[1];
610         const touch_point cur_tp_f2 = _gd->_info._finger_move[1];
611
612         const touch_point start_center = calc_center(start_tp_f1, start_tp_f2);
613         const touch_point cur_center = calc_center(cur_tp_f1, cur_tp_f2);
614
615         int diff = _gd->get_trajectory_effective_length(start_center, cur_center);
616         static int compen_x = 0;
617         static int compen_y = 0;
618
619         if (!panning_changed && diff > _gd->__CLICK_AREA * 2) {
620                 compen_x = cur_center._x - start_center._x;
621                 compen_y = cur_center._y - start_center._y;
622                 if (delta_x) *delta_x = 0;
623                 if (delta_y) *delta_y = 0;
624                 panning_changed = true;
625         } else if (panning_changed && diff > 1) {
626                 if (delta_x) *delta_x = (cur_center._x - start_center._x) - compen_x;
627                 if (delta_y) *delta_y = (cur_center._y - start_center._y) - compen_y;
628                 panning_changed = true;
629         } else {
630                 panning_changed = false;
631         }
632
633         if (!panning_changed)
634                 return false;
635
636         return true;
637 }
638
639
640 void view::gesture_processor::on_zoom_rotate(bool zoom_changed, double zoom_factor, bool rotation_changed, double rotation_angle, bool panning_changed,
641         int panning_x, int panning_y)
642 {
643         if (!zoom_changed && !rotation_changed && !panning_changed)
644                 return;
645
646         /* a. Get the initial screen coordinates of the center */
647         int center_x = 0;
648         int center_y = 0;
649         maps_view_geolocation_to_screen(_gd->_view, _gd->_info._start_view_state._center, &center_x, &center_y);
650
651         /* b. Apply the delta to the intital center coordinates */
652         if (panning_changed) {
653                 center_x -= panning_x;
654                 center_y -= panning_y;
655         }
656
657         /* c. Send event data */
658         maps_coordinates_h center = NULL;
659         maps_view_screen_to_geolocation(_gd->_view, center_x, center_y, &center);
660
661         maps_view_event_data_h ed = _maps_view_create_event_data(MAPS_VIEW_EVENT_GESTURE);
662         if (ed) {
663                 _maps_view_event_data_set_position(ed, center_x, center_y);
664                 _maps_view_event_data_set_coordinates(ed, center);
665                 _maps_view_event_data_set_fingers(ed, 2);
666
667                 if (zoom_changed) {
668                         _maps_view_event_data_set_gesture_type(ed, MAPS_VIEW_GESTURE_ZOOM);
669                         _maps_view_event_data_set_zoom_factor(ed, zoom_factor);
670                         _maps_view_event_data_set_rotation_angle(ed, 0.);
671                         _maps_view_invoke_event_callback(_gd->_view, ed);
672                 }
673                 if (rotation_changed) {
674                         _maps_view_event_data_set_gesture_type(ed, MAPS_VIEW_GESTURE_ROTATE);
675                         _maps_view_event_data_set_zoom_factor(ed, 0.);
676                         _maps_view_event_data_set_rotation_angle(ed, rotation_angle);
677                         _maps_view_invoke_event_callback(_gd->_view, ed);
678                 }
679                 maps_view_event_data_destroy(ed);
680         }
681
682         /* d. Enque the command to move the center */
683         q()->push(new session::command_view_set_center(get_maps(), _gd->_view, center));
684
685         /* e. Enqueue the detected zomm command */
686         maps_view_gesture_e gesture = MAPS_VIEW_GESTURE_SCROLL;
687         if (zoom_changed) gesture = MAPS_VIEW_GESTURE_ZOOM;
688         if (rotation_changed) gesture = MAPS_VIEW_GESTURE_ROTATE;
689
690         q()->push(construct_gesture_command(gesture, center,
691                 zoom_changed, zoom_factor, rotation_changed, rotation_angle));
692
693         maps_coordinates_destroy(center);
694 }
695
696
697 /* ---------------------------------------------------------------------------*/
698 /*    VIEW EVENT STREAM                                                       */
699 /* ---------------------------------------------------------------------------*/
700
701 view::finger_event_stream::finger_event_stream(maps_view_h v)
702         : _d(NULL)
703 {
704         /* TODO: extract in dedicated factory in maps_view.cpp */
705         /* Issuing an instance of gestuer detector */
706         _d = new gesture_detector_statemachine(v);
707         /*_d = new inertial_gesture(v);*/
708         /*_d = new gesture_detector(v);*/
709
710         /* All fingers are un-pressed initially */
711         for(int i = 0; i < MAX_FINGERS; i ++) {
712                 _finger_pressed[i] = false;
713                 _finger_moving[i] = false;
714         }
715 }
716
717 void view::finger_event_stream::set_gesture_detector(gesture_detector *d)
718 {
719         if (!d)
720                 return;
721         if (d != _d) {
722                 _d->halt_gesture();
723                 delete _d;
724                 _d = d;
725         }
726 }
727
728 view::finger_event_stream::~finger_event_stream()
729 {
730         if (_d) {
731                 _d->halt_gesture();
732                 delete _d;
733         }
734         _d = NULL;
735 }
736
737 void view::finger_event_stream::tap(Evas_Event_Mouse_Down *ev)
738 {
739         MAPS_LOGI("finger_event_stream::tap");
740         if (!ev)
741                 return;
742
743         /*
744          *  The Ecore sends "move" event before "press" event
745          *  so we have to skip this "late" event "press" for the sake of
746          *  detector simplicity
747          */
748         if (_finger_pressed[0]) {
749                 MAPS_LOGI("finger_event_stream::tap [SKIPED]");
750                 return;
751         }
752
753         /* Current touch point info */
754         const touch_point tp(ev->canvas.x, ev->canvas.y, ev->timestamp);
755
756         _finger_pressed[0] = true;
757         _finger_down[0] = tp;
758
759         /* Process first finger press */
760         _d->tap(0, tp);
761 }
762
763 void view::finger_event_stream::move(Evas_Event_Mouse_Move *ev)
764 {
765         if (!ev)
766                 return;
767
768         if (_finger_pressed[0] == false)
769                 return;
770
771         /* Current touch point info */
772         const touch_point tp(ev->cur.canvas.x, ev->cur.canvas.y, ev->timestamp);
773
774         MAPS_LOGI("finger_event_stream::move to (%d, %d)", tp._x, tp._y);
775
776         /*
777          *  We think that the movement happend when the finger moved out of
778          *  some small area.
779          */
780         if (_finger_moving[0] || _finger_moving[1]
781            || finger_dragged_enough(0, tp)) {
782                 _finger_moving[0] = true;
783
784                 /* Process finger move */
785                 _d->move(0, tp);
786         } else {
787                 MAPS_LOGI("finger_event_stream::move "
788                           "Not Moved Enough [SKIPED]");
789                 return;
790         }
791 }
792
793 void view::finger_event_stream::up(Evas_Event_Mouse_Up *ev)
794 {
795         MAPS_LOGI("finger_event_stream::up");
796         if (!ev)
797                 return;
798
799         /* Process finger up */
800         _d->up(0, view::touch_point(ev->canvas.x, ev->canvas.y, ev->timestamp));
801
802         _finger_pressed[0] = false;
803         _finger_moving[0] = false;
804         _finger_down[0].reset();
805 }
806
807 void view::finger_event_stream::multi_tap(Evas_Event_Multi_Down *ev)
808 {
809         MAPS_LOGI("finger_event_stream::multi_tap");
810         if (!ev)
811                 return;
812
813         const int finger_no = (ev->device > 0 ? 1 : 0);
814         if (finger_no >= MAX_FINGERS)
815                 return;
816
817         /* Current touch point info */
818         const touch_point tp(ev->canvas.x, ev->canvas.y, ev->timestamp);
819
820         /*
821          *  The Ecore sends "move" event before "press" event
822          *  so we have to skip this "late" event "press" for the sake of
823          *  detector simplicity
824          */
825         if (_finger_pressed[finger_no]) {
826                 MAPS_LOGI("finger_event_stream::multi_tap [SKIPED]");
827                 return;
828         }
829
830         _finger_pressed[finger_no] = true;
831         _finger_down[finger_no] = tp;
832
833         /* Process finger press */
834         _d->tap(finger_no, tp);
835 }
836
837 void view::finger_event_stream::multi_move(Evas_Event_Multi_Move *ev)
838 {
839         if (!ev)
840                 return;
841
842         const int finger_no = (ev->device > 0 ? 1 : 0);
843         if (finger_no >= MAX_FINGERS)
844                 return;
845
846         if (_finger_pressed[finger_no] == false)
847                 return;
848
849         /* Current touch point info */
850         const touch_point tp(ev->cur.canvas.x, ev->cur.canvas.y, ev->timestamp);
851
852         MAPS_LOGI("finger_event_stream::multi_move to (%d, %d)", tp._x, tp._y);
853
854         /*
855          *  We think that the movement happend when the finger moved out of
856          *  some small area.
857          */
858         if (_finger_moving[0] || _finger_moving[1]
859            || finger_dragged_enough(finger_no, tp)) {
860                 _finger_moving[finger_no] = true;
861
862                 /* Process finger move */
863                 _d->move(finger_no, tp);
864         } else {
865                 MAPS_LOGI("finger_event_stream::multi_move "
866                           "Not Moved Enough [SKIPED]");
867                 return;
868         }
869 }
870
871 void view::finger_event_stream::multi_up(Evas_Event_Multi_Up *ev)
872 {
873         MAPS_LOGI("finger_event_stream::multi_up");
874         if (!ev)
875                 return;
876
877         const int finger_no = (ev->device > 0 ? 1 : 0);
878         if (finger_no >= MAX_FINGERS)
879                 return;
880
881         /* Process finger up */
882         _d->up(finger_no, view::touch_point(ev->canvas.x,
883                                             ev->canvas.y,
884                                             ev->timestamp));
885
886         _finger_pressed[finger_no] = false;
887         _finger_moving[finger_no] = false;
888         _finger_down[finger_no].reset();
889 }
890
891
892 bool view::finger_event_stream::finger_dragged_enough(int finger_no,
893                                                       const touch_point &tp)
894 {
895         const touch_point start = _finger_down[finger_no];
896         const int trajectory =
897                 gesture_detector::get_trajectory_effective_length(tp, start);
898         return (trajectory >= gesture_detector::__CLICK_AREA);
899 }
900 //LCOV_EXCL_STOP