The source code moved from the SPIN with license changed to Flora 1.1
[apps/native/home/homescreen-efl.git] / src / livebox / grid_reposition.c
1 /*
2  * Copyright 2012  Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.1 (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://floralicense.org/license/
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 #include <math.h>
18
19 #include "livebox/grid_reposition.h"
20
21 #include "homescreen-efl.h"
22 #include "livebox/livebox_utils.h"
23 #include "livebox/livebox_animator.h"
24 #include "livebox/livebox_panel.h"
25 #include "util.h"
26 #include "conf.h"
27 #include "data_model.h"
28
29 typedef struct {
30         Evas_Object *obj;
31         Eina_Rectangle *pos_in_grid;
32 } Repositioned_Object_Data_t;
33
34 static struct {
35         Evas_Object *repositioned_object;
36         Evas_Object *current_grid;
37         Evas_Object *origination_grid;
38         Repositioned_Object_Data_t repositioned_object_data;
39         Eina_List *repositioned_objects_in_grid;
40         Anim_Data_t *ad;
41         int offset_x;
42         int offset_y;
43         bool intersection_found;
44 } s_info = {
45         .repositioned_object = NULL,
46         .current_grid = NULL,
47         .origination_grid = NULL,
48         .repositioned_object_data = {
49                 .obj = NULL,
50                 .pos_in_grid = NULL
51         },
52         .repositioned_objects_in_grid = NULL,
53         .ad = NULL,
54         .offset_x = 0,
55         .offset_y = 0,
56         .intersection_found = true
57 };
58
59 static void __grid_reposition_drop_anim_cb(Anim_Data_t **ad);
60 static bool __grid_reposition_check_grid_intersection(Evas_Object *moved_item,
61         Evas_Object *grid);
62 static bool __grid_reposition_check_item_intersection(Evas_Object *moved_item,
63         Evas_Object *grid_item);
64 static void __grid_reposition_save_item_start_data(Evas_Object *object);
65 static void __grid_reposition_make_space(void);
66 static void __grid_reposition_update_item_position(Eina_Rectangle *new_geometry);
67
68 Evas_Object *grid_reposition_get_current_glrid(void)
69 {
70         return s_info.current_grid;
71 }
72
73 Evas_Object *grid_reposition_get_repositioned_item(void)
74 {
75         return s_info.repositioned_object;
76 }
77
78 void grid_reposition_init(Evas_Object *grid, Evas_Object *repositioned_item)
79 {
80         int mx = -1;
81         int my = -1;
82         int ox = -1;
83         int oy = -1;
84         Evas *e = NULL;
85
86         if (!grid) {
87                 LOGE("grid == NULL");
88                 return;
89         }
90
91         if (!repositioned_item) {
92                 LOGE("repositioned_item == NULL");
93                 return;
94         }
95
96         e = evas_object_evas_get(repositioned_item);
97         if (!e) {
98                 LOGE("e == NULL");
99                 return;
100         }
101
102         evas_pointer_canvas_xy_get(e, &mx, &my);
103         evas_object_geometry_get(repositioned_item, &ox, &oy, NULL, NULL);
104
105         s_info.offset_x = mx - ox;
106         s_info.offset_y = my - oy;
107
108         LOGD("MOUSE: (%d, %d); OFFSET: (%d, %d)", mx, my, s_info.offset_x,
109                 s_info.offset_y);
110
111         s_info.current_grid = s_info.origination_grid = grid;
112         s_info.repositioned_object = repositioned_item;
113
114         __grid_reposition_save_item_start_data(repositioned_item);
115 }
116
117 void grid_reposition_start(void)
118 {
119         int mx = -1;
120         int my = -1;
121         Evas_Object *page = NULL;
122         Evas_Object *grid = NULL;
123
124         int x = -1, y = -1, w = -1, h = -1;
125
126         page = livebox_utils_get_selected_livebox_layout();
127         if (!page) {
128                 LOGE("page == NULL");
129                 return;
130         }
131
132         grid = livebox_utils_get_livebox_container_grid(page);
133         if (!grid) {
134                 LOGE("grid == NULL");
135                 return;
136         }
137
138
139         elm_grid_unpack(s_info.current_grid, s_info.repositioned_object);
140         evas_object_geometry_get(s_info.repositioned_object, &x, &y, &w, &h);
141         LOGD("Packing. Reposition start: %d; %d; %d; %d", x, y, w, h);
142
143         livebox_utils_get_cursor_pos(&mx, &my);
144         grid_reposition_move(mx, my);
145
146         livebox_utils_repack_grid_object(s_info.repositioned_object,
147                 livebox_utils_get_shadow(), NULL, grid);
148 }
149
150 void grid_reposition_move(int mouse_x, int mouse_y)
151 {
152         if (!s_info.repositioned_object) {
153                 LOGE("s_info.repositioned_object == NULL");
154                 return;
155         }
156
157         if (!s_info.current_grid) {
158                 LOGE("s_info.current_grid == NULL");
159                 return;
160         }
161
162         evas_object_move(s_info.repositioned_object,
163                 mouse_x - s_info.offset_x, mouse_y - s_info.offset_y);
164         s_info.intersection_found =
165         __grid_reposition_check_grid_intersection(s_info.repositioned_object,
166                 s_info.current_grid);
167
168
169         if (livebox_panel_is_add_page_selected())  {
170                 /*no intersection found but we can't drop on 'add page' page */
171                 s_info.intersection_found = true;
172                 livebox_utils_set_shadow_visibility(false);
173         } else if (!s_info.intersection_found) {
174                 /*no intersection found so we can show the shadow */
175                 livebox_utils_set_grid_object_pack(s_info.repositioned_object,
176                         livebox_utils_get_shadow(), s_info.current_grid);
177                 livebox_utils_set_shadow_visibility(true);
178         } else if (s_info.current_grid != s_info.origination_grid) {
179         /*intersection found and livebox was moved from diferent grid.
180         We can hide the shadow*/
181                 __grid_reposition_make_space();
182                 livebox_utils_set_shadow_visibility(false);
183         } else {
184         /* intersection found and livebox was moved from current grid.
185         We should display the shadow on the origination position */
186
187                 __grid_reposition_make_space();
188                 elm_grid_pack_set(livebox_utils_get_shadow(),
189                                 s_info.repositioned_object_data.pos_in_grid->x,
190                                 s_info.repositioned_object_data.pos_in_grid->y,
191                                 s_info.repositioned_object_data.pos_in_grid->w,
192                                 s_info.repositioned_object_data.pos_in_grid->h);
193
194                 livebox_utils_set_shadow_visibility(true);
195         }
196 }
197
198 void grid_reposition_end(void)
199 {
200         Eina_Rectangle *start = NULL;
201         Eina_Rectangle *end   = NULL;
202         int x = -1;
203         int y = -1;
204         int w = -1;
205         int h = -1;
206         Evas_Object *destination_grid = NULL;
207
208         if (!s_info.repositioned_object) {
209                 LOGE("s_info.repositioned_object == NULL");
210                 return;
211         }
212
213         if (!s_info.current_grid) {
214                 LOGE("s_info.current_grid == NULL");
215                 return;
216         }
217
218         if (!s_info.origination_grid) {
219                 LOGE("s_info.origination_grid == NULL");
220                 return;
221         }
222
223         s_info.intersection_found = __grid_reposition_check_grid_intersection(
224                 s_info.repositioned_object, s_info.current_grid);
225
226         if (!s_info.intersection_found) {
227                 livebox_utils_convert_size_to_grid_coord(s_info.repositioned_object,
228                         s_info.current_grid, &x, &y, &w, &h);
229                 livebox_utils_normalize_grid_pos(x, y, w, h, &x, &y);
230                 destination_grid = s_info.current_grid;
231         } else {
232                 x = s_info.repositioned_object_data.pos_in_grid->x;
233                 y = s_info.repositioned_object_data.pos_in_grid->y;
234                 w = s_info.repositioned_object_data.pos_in_grid->w;
235                 h = s_info.repositioned_object_data.pos_in_grid->h;
236                 destination_grid = s_info.origination_grid;
237         }
238
239         start = livebox_utils_get_widget_rectangle(s_info.repositioned_object);
240         end = livebox_utils_convert_virtual_grid_geo_to_screen(destination_grid,
241                         x, y, w, h);
242
243         livebox_animator_play_geometry_set(s_info.repositioned_object,
244                         LIVEBOX_REPOSITION_ANIM_TIME,
245                         ECORE_POS_MAP_DECELERATE_FACTOR, 2, 0,
246                         start, end,
247                         __grid_reposition_drop_anim_cb, &s_info.ad);
248 }
249
250 void grid_reposition_set_current_grid(Evas_Object *grid)
251 {
252         if (grid == NULL) {
253                 LOGE("grid == NULL");
254                 s_info.current_grid = s_info.origination_grid;
255                 return;
256         }
257
258         s_info.current_grid = grid;
259 }
260
261 /* ================================== intersection check =====================*/
262
263 static Eina_Rectangle *_reposition_grid_item_pos_new_get(
264         Eina_Rectangle *grid_item, Eina_Rectangle *moved_item)
265 {
266         int new_x = 0;
267         int new_y = 0;
268
269         if (!grid_item) {
270                 LOGE("grid_item == NULL");
271                 return NULL;
272         }
273
274         if (!moved_item) {
275                 LOGE("moved_item == NULL");
276                 return NULL;
277         }
278
279         if (moved_item->y - grid_item->h >= 0) {
280                 new_y = moved_item->y - grid_item->h;
281         } else {
282                 new_y = moved_item->y + grid_item->h;
283         }
284
285         if (new_y < 0 || new_y > LIVEBOX_TOTAL_ROWS)
286                 return NULL;
287
288         livebox_utils_normalize_grid_pos(grid_item->x, new_y, grid_item->w,
289                 grid_item->h, &new_x, &new_y);
290
291         return eina_rectangle_new(new_x, new_y, grid_item->w, grid_item->h);
292 }
293
294 static bool _rectangle_containment_check(Eina_Rectangle *outer,
295         Eina_Rectangle *inner)
296 {
297         if (!outer) {
298                 LOGE("outer == NULL");
299                 return false;
300         }
301
302         if (!inner) {
303                 LOGE("inner == NULL");
304                 return false;
305         }
306
307         if (inner->x >= outer->x && inner->y >= outer->y &&
308                 inner->x + inner->w <= outer->x + outer->w &&
309                 inner->y + inner->h <= outer->y + outer->h) {
310                 LOGD("RECTANGLE: [%d, %d - %d, %d] is IN [%d, %d - %d, %d]",
311                                 inner->x, inner->y, inner->w, inner->h,
312                                 outer->x, outer->y, outer->w, outer->h);
313
314                 return true;
315         } else {
316                 LOGD("RECTANGLE: [%d, %d - %d, %d] is NOT [%d, %d - %d, %d]",
317                                 inner->x, inner->y, inner->w, inner->h,
318                                 outer->x, outer->y, outer->w, outer->h);
319
320                 return false;
321         }
322 }
323
324 static void __grid_reposition_make_space(void)
325 {
326         Eina_Rectangle *repo_obj_rec = NULL;
327         Eina_Rectangle *grid_obj_rec = NULL;
328         Eina_Rectangle *grid_obj_new_pos = NULL;
329         Eina_List *items_in_grid = NULL;
330         Eina_List *l = NULL;
331         Evas_Object *obj = NULL;
332         int rx = -1, ry = -1, rw = -1, rh = -1;
333         bool contaiment = false;
334         bool intersection_in_grid = false;
335         bool can_space_make = false;
336
337         if (!s_info.current_grid) {
338                 LOGE("s_info_current_grid == NULL");
339                 return;
340         }
341
342         if (!s_info.origination_grid) {
343                 LOGE("s_info.origination_grid == NULL");
344                 return;
345         }
346
347         items_in_grid = livebox_utils_get_liveboxes_on_gird(
348                 s_info.current_grid);
349         if (!items_in_grid) {
350                 LOGE("items_in_grid == NULL");
351                 return;
352         }
353
354         livebox_utils_convert_size_to_grid_coord(s_info.repositioned_object,
355                 s_info.current_grid, &rx, &ry, &rw, &rh);
356         livebox_utils_normalize_grid_pos(rx, ry, rw, rh, &rx, &ry);
357
358         repo_obj_rec = eina_rectangle_new(rx, ry, rw, rh);
359         if (!repo_obj_rec) {
360                 LOGE("repo_obj_rec == NULL");
361                 return;
362         }
363
364         EINA_LIST_FOREACH(items_in_grid, l, obj) {
365                 if (!obj) {
366                         LOGE("obj == NULL");
367                         continue;
368                 }
369
370                 grid_obj_rec = livebox_utils_get_grid_widget_rectangle(obj);
371                 if (!grid_obj_rec) {
372                         LOGE("grid_obj_rec == NULL");
373                         continue;
374                 }
375
376                 contaiment = _rectangle_containment_check(grid_obj_rec,
377                         repo_obj_rec); /*moved obj in grid obj*/
378
379                 if (!contaiment) {
380                         contaiment = _rectangle_containment_check(repo_obj_rec,
381                                 grid_obj_rec); /*grid obj in moved obj*/
382                 }
383
384                 if (contaiment) {
385                         grid_obj_new_pos = _reposition_grid_item_pos_new_get(
386                                 grid_obj_rec, repo_obj_rec);
387
388                         if (grid_obj_new_pos &&
389                                 !eina_rectangles_intersect(grid_obj_new_pos,
390                                         repo_obj_rec)) {
391                                 intersection_in_grid =
392                                 livebox_utils_check_rect_list_grid_interesction(
393                                         grid_obj_new_pos, obj, items_in_grid);
394
395                                 /*TODO: This call is not required when we assume
396                                  that no livebox can be put side by side*/
397                                 if (!intersection_in_grid) {
398                                         elm_grid_pack_set(obj,
399                                                 grid_obj_new_pos->x,
400                                                 grid_obj_new_pos->y,
401                                                 grid_obj_new_pos->w,
402                                                 grid_obj_new_pos->h);
403                                         can_space_make = true;
404                                 } else {
405                                         can_space_make = false;
406                                         eina_rectangle_free(grid_obj_new_pos);
407                                         eina_rectangle_free(grid_obj_rec);
408                                         break;
409                                 }
410
411                                 eina_rectangle_free(grid_obj_new_pos);
412                         }
413                 }
414
415                 eina_rectangle_free(grid_obj_rec);
416         }
417
418         if (can_space_make)
419                 __grid_reposition_update_item_position(repo_obj_rec);
420
421         eina_rectangle_free(repo_obj_rec);
422         eina_list_free(items_in_grid);
423 }
424
425 static void __grid_reposition_save_item_start_data(Evas_Object *object)
426 {
427         int x = -1, y = -1, w = -1, h = -1;
428
429         if (!object) {
430                 LOGE("object == NULL");
431                 return;
432         }
433
434         s_info.repositioned_object_data.obj = object;
435         elm_grid_pack_get(object, &x, &y, &w, &h);
436
437         s_info.repositioned_object_data.pos_in_grid = eina_rectangle_new(x, y,
438                                                         w, h);
439 }
440
441 static void __grid_reposition_update_item_position(Eina_Rectangle *new_geometry)
442 {
443         if (!new_geometry) {
444                 LOGE("new_geometry == NULL");
445                 return;
446         }
447
448         if (!s_info.repositioned_object_data.pos_in_grid) {
449                 LOGE("s_info.repositioned_object_data.pos_in_grid == NULL");
450                 return;
451         }
452
453         s_info.repositioned_object_data.pos_in_grid->x = new_geometry->x;
454         s_info.repositioned_object_data.pos_in_grid->y = new_geometry->y;
455         s_info.repositioned_object_data.pos_in_grid->w = new_geometry->w;
456         s_info.repositioned_object_data.pos_in_grid->h = new_geometry->h;
457 }
458
459 static bool __grid_reposition_check_grid_intersection(Evas_Object *moved_item,
460         Evas_Object *grid)
461 {
462         Eina_List *grid_items = NULL;
463         Eina_List *l = NULL;
464         Evas_Object *grid_item = NULL;
465
466         if (!moved_item) {
467                 LOGE("moved_item == NULL");
468                 return false;
469         }
470
471         if (!grid) {
472                 LOGE("grid == NULL");
473                 return false;
474         }
475
476         grid_items = livebox_utils_get_liveboxes_on_gird(grid);
477         if (!grid_items) {
478                 LOGE("grid_items == NULL");
479                 return false;
480         }
481
482         EINA_LIST_FOREACH(grid_items, l, grid_item) {
483                 if (!grid_item) {
484                         LOGE("item == NULL");
485                         continue;
486                 }
487
488                 if (__grid_reposition_check_item_intersection(moved_item,
489                         grid_item) == true) {
490                         eina_list_free(grid_items);
491                         return true;
492                 }
493         }
494
495         eina_list_free(grid_items);
496         return false;
497 }
498
499 static Eina_Rectangle *_normalized_rectangle_get(Evas_Object *obj)
500 {
501         int x = -1;
502         int y = -1;
503         int w = -1;
504         int h = -1;
505         Eina_Rectangle *rect = NULL;
506
507         if (!s_info.current_grid) {
508                 LOGE("s_info.current_grid == NULL");
509                 return NULL;
510         }
511
512         if (!obj) {
513                 LOGE("obj == NULL");
514                 return NULL;
515         }
516
517         livebox_utils_convert_size_to_grid_coord(obj, s_info.current_grid,
518                 &x, &y, &w, &h);
519         livebox_utils_normalize_grid_pos(x, y, w, h, &x, &y);
520         rect = livebox_utils_convert_virtual_grid_geo_to_screen(s_info.current_grid,
521                 x, y, w, h);
522
523         if (!rect) {
524                 LOGE("rect == NULL");
525                 return NULL;
526         }
527
528         return rect;
529 }
530
531 static bool __grid_reposition_check_item_intersection(Evas_Object *moved_item,
532         Evas_Object *grid_item)
533 {
534         Eina_Rectangle *moved_item_rec = NULL;
535         Eina_Rectangle *grid_item_rec  = NULL;
536         bool ret = false;
537
538         if (!moved_item) {
539                 LOGE("moved_item == NULL");
540                 return false;
541         }
542
543         if (!grid_item) {
544                 LOGE("grid_item == NULL");
545                 return false;
546         }
547
548         moved_item_rec = _normalized_rectangle_get(moved_item);
549         if (!moved_item_rec) {
550                 LOGE("moved_item_rec == NULL");
551                 return false;
552         }
553
554         grid_item_rec =  livebox_utils_get_widget_rectangle(grid_item);
555         if (!grid_item_rec) {
556                 LOGE("grid_item_rec == NULL");
557                 eina_rectangle_free(moved_item_rec);
558                 return false;
559         }
560
561         ret = eina_rectangle_intersection(moved_item_rec, grid_item_rec);
562
563         eina_rectangle_free(moved_item_rec);
564         eina_rectangle_free(grid_item_rec);
565
566         return ret;
567 }
568
569 static void _grid_reposition_update_data_model(Evas_Object *page)
570 {
571         Eina_List *items = NULL;
572         Tree_node_t *node = NULL;
573         Tree_node_t *page_node = NULL;
574         int x = 0, y = 0, w = 0, h = 0;
575         Eina_List *l;
576         Evas_Object *livebox;
577
578         LOGI("DATA MODEL REPOSITION START");
579
580         elm_grid_pack_get(s_info.repositioned_object, &x, &y, &w, &h);
581         node = evas_object_data_get(s_info.repositioned_object, KEY_ICON_DATA);
582         if (!node) {
583                 LOGE("node == NULL");
584                 return;
585         }
586
587         page_node = evas_object_data_get(page, KEY_ICON_DATA);
588         if (!page_node) {
589                 LOGE("obj_node == NULL");
590                 return;
591         }
592
593         data_model_reposition_widget(page_node, node);
594
595         items = livebox_utils_get_liveboxes_on_page_list(page);
596         if (!items) {
597                 LOGD("items == NULL");
598                 return;
599         }
600
601         EINA_LIST_FOREACH(items, l, livebox) {
602                 if (!livebox) {
603                         LOGE("livebox == NULL");
604                         continue;
605                 }
606
607                 elm_grid_pack_get(livebox, &x, &y, &w, &h);
608                 node = evas_object_data_get(livebox, KEY_ICON_DATA);
609                 if (!node) {
610                         LOGE("node == NULL");
611                         continue;
612                 }
613
614                 data_model_resize_widget(node, x, y, w, h);
615         }
616
617         home_screen_print_tree();
618 }
619
620 static void __grid_reposition_drop_anim_cb(Anim_Data_t **ad)
621 {
622         Evas_Object *page = NULL;
623
624         LOGI("DROP ANIM END");
625
626         if (!s_info.current_grid || !s_info.origination_grid ||
627                 !s_info.repositioned_object) {
628                 LOGE("s_info.current_grid == %p; s_info.origination_grid == %p;\
629                         s_info.repositioned_object == %p", s_info.current_grid,
630                         s_info.origination_grid, s_info.repositioned_object);
631
632                 s_info.repositioned_object = NULL;
633                 s_info.current_grid     = NULL;
634                 livebox_animator_del_grid(ad);
635                 s_info.intersection_found = false;
636                 eina_rectangle_free(
637                         s_info.repositioned_object_data.pos_in_grid);
638         }
639
640         livebox_utils_repack_grid_object(NULL, livebox_utils_get_shadow(),
641                 s_info.current_grid, NULL);
642
643         if (!s_info.intersection_found) {
644                 livebox_utils_repack_grid_object(s_info.repositioned_object,
645                                 s_info.repositioned_object,
646                                 NULL,
647                                 s_info.current_grid);
648
649                 page = livebox_scroller_get_page();
650         } else {
651                 elm_grid_pack(s_info.origination_grid,
652                         s_info.repositioned_object,
653                         s_info.repositioned_object_data.pos_in_grid->x,
654                         s_info.repositioned_object_data.pos_in_grid->y,
655                         s_info.repositioned_object_data.pos_in_grid->w,
656                         s_info.repositioned_object_data.pos_in_grid->h);
657
658                 page = livebox_utils_get_prev_livebox_layout();
659         }
660
661         if (!page) {
662                 LOGE("page == NULL");
663                 s_info.repositioned_object = NULL;
664                 s_info.current_grid     = NULL;
665
666                 livebox_animator_del_grid(ad);
667                 s_info.intersection_found = false;
668                 eina_rectangle_free(
669                         s_info.repositioned_object_data.pos_in_grid);
670                 return;
671         }
672
673         _grid_reposition_update_data_model(page);
674
675         s_info.repositioned_object = NULL;
676         s_info.current_grid     = NULL;
677
678         livebox_animator_del_grid(ad);
679         s_info.intersection_found = false;
680         eina_rectangle_free(s_info.repositioned_object_data.pos_in_grid);
681 }