base: find the item to be focused using media id
[profile/tv/apps/native/air_mediahub.git] / src / util / listmgr.c
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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 #include <Elementary.h>
18 #include <media_content.h>
19 #include <app_debug.h>
20 #include <app_media.h>
21 #include <gridmgr.h>
22 #include <inputmgr.h>
23
24 #include "define.h"
25 #include "data/mediadata.h"
26 #include "util/listmgr.h"
27 #include "util/util.h"
28
29 #define MAX_GRID_WIDTH 1858
30
31 #define SCROLLER_STEP_SIZE(x) ((x) + 10)
32
33 enum _object_type {
34         LISTMGR_SCROLLER = 0,
35         LISTMGR_INDEX,
36         LISTMGR_PLAY_INFO,
37         LISTMGR_GRID
38 };
39
40 struct listmgr {
41         Evas_Object *base;
42         Evas_Object *scr;
43         Evas_Object *box;
44         Evas_Object *play_info;
45         Evas_Object *focused;
46
47         Elm_Object_Item *focused_it;
48         Elm_Object_Item *selected_it;
49
50         struct gridmgr *gmgr;
51         struct listmgr_data *data;
52
53         Eina_Bool show_play_info;
54         Eina_Bool focus_state;
55 };
56
57 static void _key_down_cb(int id, void *data, Evas *e, Evas_Object *obj,
58                         Evas_Event_Key_Down *ev)
59 {
60         struct listmgr *lmgr;
61         struct grid_ops *gops;
62
63         if (!data || !obj || !ev)
64                 return;
65
66         lmgr = data;
67
68         switch (id) {
69         case LISTMGR_INDEX:
70                 if (!strcmp(ev->keyname, KEY_BACK) ||
71                         !strcmp(ev->keyname, KEY_BACK_REMOTE)) {
72                         elm_object_focus_next(obj, ELM_FOCUS_UP);
73                 } else if (!strcmp(ev->keyname, KEY_DOWN)) {
74                         Evas_Object *grid;
75                         Elm_Object_Item *it;
76
77                         if (lmgr->play_info == lmgr->focused)
78                                 break;
79
80                         grid = elm_object_part_content_get(lmgr->focused,
81                                                 PART_ITEM_CONTENT);
82                         it = elm_gengrid_first_item_get(grid);
83                         if (it)
84                                 elm_object_item_focus_set(it, EINA_TRUE);
85                 }
86
87                 break;
88         case LISTMGR_PLAY_INFO:
89         case LISTMGR_GRID:
90                 if (!strcmp(ev->keyname, KEY_BACK) ||
91                         !strcmp(ev->keyname, KEY_BACK_REMOTE))
92                         elm_object_focus_next(obj, ELM_FOCUS_UP);
93                 else {
94                         gops = lmgr->data->gops;
95                         if (gops->key_down_cb)
96                                 gops->key_down_cb(gops->ops_data,
97                                                         lmgr->focused_it, ev);
98                 }
99
100                 break;
101         default:
102                 break;
103         }
104 }
105
106 static void _mouse_move_cb(int id, void *data, Evas *e, Evas_Object *obj,
107                         Evas_Event_Mouse_Move *ev)
108 {
109         Elm_Object_Item *it;
110         struct listmgr *lmgr;
111
112         if (!data || !obj || !ev)
113                 return;
114
115         lmgr = data;
116
117         switch (id) {
118         case LISTMGR_INDEX:
119         case LISTMGR_PLAY_INFO:
120                 if (!elm_object_focus_get(obj)) {
121                         lmgr->focus_state = EINA_TRUE;
122                         elm_object_focus_set(obj, EINA_TRUE);
123                 }
124
125                 break;
126         case LISTMGR_GRID:
127                 it = elm_gengrid_at_xy_item_get(obj, ev->cur.canvas.x,
128                                         ev->cur.canvas.y, NULL, NULL);
129
130                 if (!it)
131                         return;
132
133                 if (!elm_object_item_focus_get(it)) {
134                         lmgr->focus_state = EINA_TRUE;
135                         elm_object_item_focus_set(it, EINA_TRUE);
136                 }
137
138                 break;
139         default:
140                 break;
141         }
142 }
143
144 static void _clicked_cb(int id, void *data, Evas_Object *obj)
145 {
146         if (!obj)
147                 return;
148
149         switch (id) {
150         case LISTMGR_PLAY_INFO:
151                 elm_object_signal_emit(obj, SIG_BTN_CLICKED, SIG_SOURCE_SRC);
152                 break;
153         default:
154                 break;
155         }
156 }
157
158 static void _focused_cb(int id, void *data, Evas_Object *obj,
159                         Elm_Object_Item *it)
160 {
161         Evas_Object *btn;
162         struct listmgr *lmgr;
163
164         if (!data || !obj)
165                 return;
166
167         lmgr = data;
168
169         switch (id) {
170         case LISTMGR_SCROLLER:
171                 if (lmgr->focus_state)
172                         break;
173
174                 btn = elm_object_part_content_get(lmgr->focused,
175                                 PART_ITEM_TITLE);
176                 if (!btn) {
177                         _ERR("failed to get button object");
178                         return;
179                 }
180
181                 lmgr->focus_state = EINA_TRUE;
182                 elm_object_focus_set(btn, EINA_TRUE);
183
184                 break;
185         case LISTMGR_INDEX:
186         case LISTMGR_PLAY_INFO:
187         case LISTMGR_GRID:
188                 lmgr->focused = elm_object_parent_widget_get(obj);
189                 elm_object_signal_emit(lmgr->data->menu_btn, SIG_BTN_SELECTED,
190                                         SIG_SOURCE_SRC);
191                 break;
192         default:
193                 break;
194         }
195 }
196
197 static void _grid_selected(void *data)
198 {
199         struct listmgr *lmgr;
200         struct grid_ops *gops;
201
202         if (!data) {
203                 _ERR("invalid argument");
204                 return;
205         }
206
207         lmgr = data;
208
209         gops = lmgr->data->gops;
210
211         if (gops->selected_cb)
212                 gops->selected_cb(gops->ops_data, lmgr->selected_it);
213 }
214
215 static void _grid_selected_cb(int id, void *data, Evas_Object *obj,
216                         Elm_Object_Item *it)
217 {
218         struct listmgr *lmgr;
219
220         if (!data || !it)
221                 return;
222
223         lmgr = data;
224
225         lmgr->selected_it = it;
226
227         ecore_job_add(_grid_selected, lmgr);
228 }
229
230 static void _play_info_selected_cb(void *data, Evas_Object *obj,
231                         const char *emission, const char *source)
232 {
233         struct listmgr *lmgr;
234         struct play_info_ops *pops;
235
236         if (!data || !obj)
237                 return;
238
239         lmgr = data;
240         pops = lmgr->data->pops;
241
242         if (pops->selected_cb)
243                 pops->selected_cb(pops->ops_data, obj);
244 }
245
246 static void _item_focused_cb(void *data, Elm_Object_Item *it,
247                         const char *emission, const char *source)
248 {
249         struct listmgr *lmgr;
250
251         if (!data || !it)
252                 return;
253
254         lmgr = data;
255
256         lmgr->focused_it = it;
257 }
258
259 static void _add_grid_item_callback(Elm_Object_Item *it, void *data)
260 {
261         elm_object_item_signal_callback_add(it, SIG_ITEM_FOCUSED,
262                                 SIG_SOURCE_EDC, _item_focused_cb, data);
263 }
264
265 static void _delete_grid_item_callback(Elm_Object_Item *it)
266 {
267         elm_object_item_signal_callback_del(it, SIG_ITEM_FOCUSED,
268                                 SIG_SOURCE_EDC, _item_focused_cb);
269 }
270
271 static void _realized_cb(int id, void *data, Evas_Object *obj,
272                         Elm_Object_Item *it)
273 {
274         struct listmgr *lmgr;
275
276         if (!data || !it)
277                 return;
278
279         lmgr = data;
280
281         _add_grid_item_callback(it, lmgr);
282 }
283
284 static void _unrealized_cb(int id, void *data, Evas_Object *obj,
285                         Elm_Object_Item *it)
286 {
287         if (!it)
288                 return;
289
290         _delete_grid_item_callback(it);
291 }
292
293 static input_handler _grid_handler = {
294         .key_down = _key_down_cb,
295         .mouse_move = _mouse_move_cb,
296         .focused = _focused_cb,
297         .selected = _grid_selected_cb,
298         .realized = _realized_cb,
299         .unrealized = _unrealized_cb
300 };
301
302 static input_handler _handler = {
303         .key_down = _key_down_cb,
304         .mouse_move = _mouse_move_cb,
305         .clicked = _clicked_cb,
306         .focused = _focused_cb
307 };
308
309 static Evas_Object *_get_box_first_item(Evas_Object *box)
310 {
311         Eina_List *list;
312
313         list = elm_box_children_get(box);
314         if (!list)
315                 return NULL;
316
317         return (Evas_Object *)eina_list_nth(list, 0);
318 }
319
320 static int _get_grid_width(int count, int num_item, int item_x)
321 {
322         int num_col, width;
323
324         num_col = count / num_item;
325         if (count % num_item > 0)
326                 num_col++;
327
328         width = num_col * item_x;
329         if (width > MAX_GRID_WIDTH)
330                 width = MAX_GRID_WIDTH;
331
332         return width;
333 }
334
335 static bool _draw_play_info(struct listmgr *lmgr)
336 {
337         Evas_Object *ly, *index, *content;
338         struct play_info_ops *pops;
339
340         ly = elm_layout_add(lmgr->box);
341         if (!ly) {
342                 _ERR("failed to create layout object");
343                 return false;
344         }
345
346         elm_layout_file_set(ly, EDJEFILE, GRP_LIST_ITEM);
347
348         pops = lmgr->data->pops;
349
350         if (!pops->draw(pops->ops_data, ly)) {
351                 _ERR("failed to draw play info item");
352                 evas_object_del(ly);
353                 return false;
354         }
355
356         index = elm_object_part_content_get(ly, PART_ITEM_TITLE);
357
358         inputmgr_add_callback(index, LISTMGR_INDEX, &_handler, lmgr);
359         elm_object_focus_next_object_set(index, lmgr->data->menu_btn,
360                                         ELM_FOCUS_UP);
361
362         content = elm_object_part_content_get(ly, PART_ITEM_CONTENT);
363
364         elm_object_focus_next_object_set(content, index, ELM_FOCUS_UP);
365
366         inputmgr_add_callback(content, LISTMGR_PLAY_INFO, &_handler, lmgr);
367         elm_object_signal_callback_add(content, SIG_ITEM_SELECTED,
368                                 SIG_SOURCE_EDC, _play_info_selected_cb, lmgr);
369
370         lmgr->play_info = ly;
371
372         return true;
373 }
374
375 static Evas_Object *_draw_list_item(struct listmgr *lmgr, struct group_info *gi)
376 {
377         Evas_Object *ly, *btn, *grid;
378         struct listmgr_data *data;
379         int width;
380         char buf[128];
381
382         data = lmgr->data;
383
384         ly = elm_layout_add(lmgr->box);
385         if (!ly) {
386                 _ERR("failed to create layout object");
387                 return NULL;
388         }
389
390         elm_layout_file_set(ly, EDJEFILE, GRP_LIST_ITEM);
391
392         btn = elm_button_add(ly);
393         if (!btn) {
394                 _ERR("failed to create button object");
395                 evas_object_del(ly);
396                 return NULL;
397         }
398
399         elm_object_style_set(btn, STYLE_BTN_INDEX);
400
401         snprintf(buf, sizeof(buf), " %s ", gi->name);
402         elm_object_text_set(btn, buf);
403
404         elm_object_focus_next_object_set(btn,
405                                 lmgr->data->menu_btn, ELM_FOCUS_UP);
406
407         inputmgr_add_callback(btn, LISTMGR_INDEX, &_handler, lmgr);
408
409         grid = util_add_gengrid(ly, data->grid_item_x,
410                                 data->grid_item_y, EINA_TRUE);
411         if (!grid) {
412                 _ERR("failed to create gengrid object");
413                 evas_object_del(ly);
414                 return NULL;
415         }
416
417         if (!gridmgr_add_grid(lmgr->gmgr, gi->name, grid, data->gops->gclass)) {
418                 _ERR("failed to add grid");
419                 evas_object_del(ly);
420                 return NULL;
421         }
422
423         width = _get_grid_width(eina_list_count(gi->list), data->grid_num_item,
424                                 data->grid_item_x);
425
426         evas_object_size_hint_min_set(grid, width,
427                                 data->grid_num_item * data->grid_item_y);
428
429         elm_object_focus_next_object_set(grid, btn, ELM_FOCUS_UP);
430
431         inputmgr_add_callback(grid, LISTMGR_GRID, &_grid_handler, lmgr);
432
433         elm_object_part_content_set(ly, PART_ITEM_TITLE, btn);
434         elm_object_part_content_set(ly, PART_ITEM_CONTENT, grid);
435
436         return ly;
437 }
438
439 bool _focus_to_content(struct listmgr *lmgr, Evas_Object *base)
440 {
441         Evas_Object *content;
442
443         content = elm_object_part_content_get(base, PART_ITEM_CONTENT);
444         if (!content) {
445                 _ERR("failed to get content");
446                 return false;
447         }
448
449         lmgr->focus_state = EINA_TRUE;
450         elm_object_focus_set(content, EINA_TRUE);
451
452         return true;
453 }
454
455 Elm_Object_Item *_get_grid_item(Evas_Object *grid, const char *id)
456 {
457         Elm_Object_Item *it;
458         app_media *am;
459         app_media_info *mi;
460
461         for (it = elm_gengrid_first_item_get(grid); it;
462                         it = elm_gengrid_item_next_get(it)) {
463                 am = elm_object_item_data_get(it);
464                 if (!am)
465                         continue;
466
467                 mi = app_media_get_info(am);
468                 if (!mi)
469                         continue;
470
471                 if (!strcmp(id, mi->media_id))
472                         return it;
473         }
474
475         return NULL;
476 }
477
478 void listmgr_set_focus_state(struct listmgr *lmgr, Eina_Bool state)
479 {
480         if (!lmgr) {
481                 _ERR("failed to get listmgr");
482                 return;
483         }
484
485         lmgr->focus_state = state;
486 }
487
488 bool listmgr_focus_play_info(struct listmgr *lmgr)
489 {
490         if (!lmgr) {
491                 _ERR("failed to get listmgr");
492                 return false;
493         }
494
495         if (!_focus_to_content(lmgr, lmgr->play_info)) {
496                 _ERR("failed to focus to content");
497                 return false;
498         }
499
500         return true;
501 }
502
503 bool listmgr_focus_content_list(struct listmgr *lmgr, const char *id)
504 {
505         Eina_List *item, *l;
506         Evas_Object *ly, *grid;
507         Elm_Object_Item *it;
508
509         if (!lmgr) {
510                 _ERR("failed to get listmgr");
511                 return false;
512         }
513
514         if (!id) {
515                 _ERR("invalid argument");
516                 return false;
517         }
518
519         item = elm_box_children_get(lmgr->box);
520
521         grid = NULL;
522         it = NULL;
523         EINA_LIST_FOREACH(item, l, ly) {
524                 grid = elm_object_part_content_get(ly, PART_ITEM_CONTENT);
525                 if (!grid) {
526                         _ERR("failed to get grid object");
527                         return false;
528                 }
529
530                 it = _get_grid_item(grid, id);
531                 if (it)
532                         break;
533         }
534
535         if (!it) {
536                 _ERR("failed to get current grid item");
537                 return false;
538         }
539
540         elm_gengrid_item_bring_in(it, ELM_GENGRID_ITEM_SCROLLTO_MIDDLE);
541
542         lmgr->focus_state = EINA_TRUE;
543         elm_object_item_focus_set(it, EINA_TRUE);
544
545         return true;
546 }
547
548 void _update_grid_realized_items(struct listmgr *lmgr, Eina_List *list)
549 {
550         Eina_List *l;
551         Elm_Object_Item *it;
552
553         EINA_LIST_FOREACH(list, l, it) {
554                 _delete_grid_item_callback(it);
555
556                 elm_gengrid_item_update(it);
557                 _add_grid_item_callback(it, lmgr);
558         }
559 }
560
561 bool listmgr_update_content_item(struct listmgr *lmgr)
562 {
563         Eina_List *box_item, *grid_realized;
564         Eina_List *l;
565         Evas_Object *ly, *grid;
566
567         if (!lmgr) {
568                 _ERR("failed to get listmgr");
569                 return false;
570         }
571
572         box_item = elm_box_children_get(lmgr->box);
573
574         EINA_LIST_FOREACH(box_item, l, ly) {
575                 grid = elm_object_part_content_get(ly, PART_ITEM_CONTENT);
576                 if (!grid) {
577                         _ERR("failed to get grid object");
578                         return false;
579                 }
580
581                 grid_realized = elm_gengrid_realized_items_get(grid);
582                 _update_grid_realized_items(lmgr, grid_realized);
583         }
584
585         return true;
586 }
587
588 bool listmgr_update_content_list(struct listmgr *lmgr, Eina_List *list)
589 {
590         Evas_Object *ly, *btn, *grid;
591         Eina_List *l;
592         struct group_info *gi;
593
594         if (!lmgr) {
595                 _ERR("invalid argument");
596                 return false;
597         }
598
599         ly = NULL;
600         EINA_LIST_FOREACH(list, l, gi) {
601                 ly = _draw_list_item(lmgr, gi);
602                 if (!ly) {
603                         _ERR("failed to draw list item");
604                         return false;
605                 }
606
607                 if (!gridmgr_append_list(lmgr->gmgr, gi->name, gi->list)) {
608                         _ERR("failed to append list in grid");
609                         return false;
610                 }
611
612                 evas_object_show(ly);
613
614                 elm_box_pack_end(lmgr->box, ly);
615         }
616
617         btn = elm_object_part_content_get(ly, PART_ITEM_TITLE);
618         elm_object_focus_next_object_set(btn, btn, ELM_FOCUS_RIGHT);
619
620         grid = elm_object_part_content_get(ly, PART_ITEM_CONTENT);
621         elm_object_focus_next_object_set(grid, grid, ELM_FOCUS_RIGHT);
622
623         lmgr->focused = _get_box_first_item(lmgr->box);
624
625         elm_object_focus_allow_set(lmgr->scr, EINA_TRUE);
626
627         return true;
628 }
629
630 bool listmgr_update_play_info(struct listmgr *lmgr, app_media_info *info)
631 {
632         struct play_info_ops *pops;
633
634         if (!lmgr) {
635                 _ERR("invalid argument");
636                 return false;
637         }
638
639         if (!info) {
640                 if (lmgr->show_play_info) {
641                         if (lmgr->play_info == lmgr->focused)
642                                 lmgr->focused = _get_box_first_item(lmgr->box);
643
644                         elm_box_unpack(lmgr->box, lmgr->play_info);
645                         evas_object_hide(lmgr->play_info);
646                         lmgr->show_play_info = EINA_FALSE;
647                 }
648                 return true;
649         }
650
651         pops = lmgr->data->pops;
652
653         if (!pops->update(pops->ops_data, lmgr->play_info)) {
654                 _ERR("failed to update play info");
655                 return false;
656         }
657
658         if (!lmgr->show_play_info) {
659                 elm_box_pack_start(lmgr->box, lmgr->play_info);
660                 evas_object_size_hint_align_set(lmgr->play_info, 0.0, 0.0);
661                 evas_object_show(lmgr->play_info);
662
663                 lmgr->show_play_info = EINA_TRUE;
664                 lmgr->focused = lmgr->play_info;
665         }
666
667         return true;
668 }
669
670 bool listmgr_draw_list_area(struct listmgr *lmgr)
671 {
672         Evas_Object *scr, *box;
673         struct listmgr_data *data;
674
675         if (!lmgr) {
676                 _ERR("invalid argument");
677                 return false;
678         }
679
680         data = lmgr->data;
681
682         scr = util_add_scroller(lmgr->base);
683         if (!scr) {
684                 _ERR("failed to create scroller object");
685                 return false;
686         }
687
688         elm_scroller_step_size_set(scr, SCROLLER_STEP_SIZE(
689                                 data->box_padding + data->grid_item_x), 0);
690
691         inputmgr_add_callback(scr, LISTMGR_SCROLLER, &_handler, lmgr);
692
693         box = util_add_box(scr);
694         if (!box) {
695                 _ERR("failed to create box object");
696                 evas_object_del(scr);
697                 return false;
698         }
699
700         elm_box_padding_set(box, data->box_padding, 0);
701
702         evas_object_show(box);
703         elm_object_content_set(scr, box);
704
705         elm_object_part_content_set(lmgr->base, PART_CONTENT, scr);
706
707         lmgr->scr = scr;
708         lmgr->box = box;
709
710         if (data->pops) {
711                 if (!_draw_play_info(lmgr)) {
712                         _ERR("failed to draw play info");
713                         evas_object_del(scr);
714                         return false;
715                 }
716         }
717
718         elm_object_focus_next_object_set(data->menu_btn, scr, ELM_FOCUS_DOWN);
719
720         elm_object_focus_allow_set(scr, EINA_FALSE);
721
722         return true;
723 }
724
725 struct listmgr *listmgr_create(Evas_Object *base, void *data)
726 {
727         struct gridmgr *gmgr;
728         struct listmgr *lmgr;
729
730         if (!base || !data) {
731                 _ERR("invalid argument");
732                 return NULL;
733         }
734
735         lmgr = calloc(1, sizeof(*lmgr));
736         if (!lmgr) {
737                 _ERR("failed to allocate listmgr");
738                 return NULL;
739         }
740
741         gmgr = gridmgr_create();
742         if (!gmgr) {
743                 _ERR("failed to create gridmgr");
744                 free(lmgr);
745                 return false;
746         }
747
748         lmgr->base = base;
749         lmgr->gmgr = gmgr;
750         lmgr->data = data;
751         lmgr->show_play_info = EINA_FALSE;
752         lmgr->focus_state = EINA_FALSE;
753
754         return lmgr;
755 }
756
757 void listmgr_destroy(struct listmgr *lmgr)
758 {
759         Eina_List *item, *l;
760         Evas_Object *ly, *btn, *content, *grid;
761
762         if (!lmgr) {
763                 _ERR("invalid argument");
764                 return;
765         }
766
767         if (lmgr->play_info) {
768                 btn = elm_object_part_content_get(lmgr->play_info,
769                                         PART_ITEM_TITLE);
770                 content = elm_object_part_content_get(lmgr->play_info,
771                                         PART_ITEM_CONTENT);
772
773                 inputmgr_remove_callback(btn, &_handler);
774                 inputmgr_remove_callback(content, &_handler);
775
776                 elm_object_signal_callback_del(content, SIG_ITEM_SELECTED,
777                                         SIG_SOURCE_EDC, _play_info_selected_cb);
778
779                 elm_box_unpack(lmgr->box, lmgr->play_info);
780                 evas_object_del(lmgr->play_info);
781         }
782
783         item = elm_box_children_get(lmgr->box);
784         EINA_LIST_FOREACH(item, l, ly) {
785                 btn = elm_object_part_content_get(ly, PART_ITEM_TITLE);
786                 grid = elm_object_part_content_get(ly, PART_ITEM_CONTENT);
787
788                 inputmgr_remove_callback(btn, &_handler);
789                 inputmgr_remove_callback(grid, &_grid_handler);
790         }
791
792         inputmgr_remove_callback(lmgr->scr, &_handler);
793
794         gridmgr_destroy(lmgr->gmgr);
795
796         free(lmgr);
797 }