Fix focus error after context view is closed
[profile/tv/apps/native/musicplayer.git] / src / views / album-layout.cpp
1 /*
2  * Copyright (c) 2014 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 <Eina.h>
19
20 #include <AppCommon.h>
21 #include <InputHandler.h>
22 #include <SortMgr.h>
23
24 #include "dbg.h"
25 #include "i18n.h"
26 #include "define.h"
27 #include "common.h"
28 #include "album_info.h"
29 #include "song_info.h"
30 #include "music-controller.h"
31 #include "LayoutMgr.h"
32 #include "common-ui.h"
33 #include "BaseView.h"
34 #include "BaseLayout.h"
35 #include "ExtBaseLayout.h"
36 #include "ViewMgr.h"
37 #include "album-layout.h"
38 #include "album-songs-layout.h"
39 #include "base-view.h"
40 #include "Info.h"
41
42 #define GENGRID_ITEM_SIZE_W (263+20)
43 #define GENGRID_ITEM_SIZE_H (359+20)
44
45
46 enum EObjectType {
47         ALBUM_LAYOUT,
48         ALBUM_GENGRID,
49 };
50
51 struct SAlbumItemInfo {
52         CAlbumInfo *alinfo;
53         Elm_Object_Item *item;
54 };
55
56 struct SCallback {
57         void(*cbHandleEmptyStatus)(void *cookie, bool emptyStatus);
58         void *cookie;
59 };
60
61 struct SAlbumLayout {
62         Evas_Object *win;
63         Evas_Object *base;
64         Evas_Object *grid;
65         Elm_Object_Item *focusedItem;
66         Eina_List *alblist;
67         Eina_List *it_infolist;
68         Elm_Gengrid_Item_Class *item_class;
69         CMusicController *mhandle;
70         CLayoutMgr *lmgr;
71         CViewMgr *vmgr;
72         SContentInfo *ctxtinfo;
73         char *album_id;
74         CAlbumSongsLayout *layoutAlbumSongs;
75         SCallback callback;
76
77         SAlbumLayout() {
78                 memset(this, 0, sizeof(SAlbumLayout));
79         }
80         
81         ~SAlbumLayout() {
82         }
83 };
84
85
86 char *CAlbumLayout::sm_CbGetGridItemText(void *data, Evas_Object *obj, const char *part)
87 {
88         SAlbumItemInfo *itinfo = (SAlbumItemInfo *)data;
89         CAlbumInfo *alinfo = NULL;
90         char *txt = NULL;
91
92         if (!itinfo || !obj)
93                 return NULL;
94
95         alinfo = itinfo->alinfo;
96         txt = NULL;
97
98         if (!strcmp(part, "elm.text"))
99                 txt = alinfo->Name();
100         else if (!strcmp(part, "elm.text1"))
101                 txt = alinfo->Artist();
102
103         if (txt)
104                 return strdup(txt);
105
106         return NULL;
107 }
108
109
110 Evas_Object *CAlbumLayout::sm_CbGetGridItemContent(void *data, Evas_Object *obj, const char *part)
111 {
112         SAlbumItemInfo *itinfo = (SAlbumItemInfo *)data;
113         CAlbumInfo *alinfo = NULL;
114         char *path = NULL;
115         Evas_Object *img = NULL;
116         char buf[MAX_LENGTH];
117
118         if (!itinfo || !obj)
119                 return NULL;
120
121         alinfo = itinfo->alinfo;
122
123         if (!strcmp(part, "elm.swallow.icon")) {
124                 img = elm_image_add(obj);
125                 if (!img)
126                         return NULL;
127
128                 path = alinfo->ThumbnailPath();
129                 if (!path) {
130                         snprintf(buf, sizeof(buf), "%s/%s", IMAGEDIR,
131                                 MUSIC_IMAGE_DEFAULT_THUMB_126);
132                         elm_image_file_set(img, buf, "NULL");
133                         elm_image_no_scale_set(img, EINA_TRUE);
134                 }
135                 else {
136                         elm_image_file_set(img, path, "NULL");
137                         elm_image_aspect_fixed_set(img, EINA_FALSE);
138                 }
139                 return img;
140         }
141
142         return NULL;
143 }
144
145
146 void CAlbumLayout::sm_CbRemoveGridItem(void *data, Evas_Object *obj)
147 {
148         free(data);
149 }
150
151
152 void CAlbumLayout::sm_CbCtxtUpdate(void *dt, enum EActionType type, int lid)
153 {
154         CAlbumLayout *root = (CAlbumLayout*)dt;
155
156         if (root)
157                 root->m_OnCtxtUpdate(type, lid);
158 }
159
160
161 void CAlbumLayout::m_OnCtxtUpdate(EActionType type, int lid)
162 {
163         Eina_List *list = NULL;
164         CSongInfo *sinfo = NULL;
165         EAddType mode;
166
167         if (!m->ctxtinfo || !m->ctxtinfo->context)
168                 return;
169
170         if (type == ACTION_TYPE_ADDNEXT)
171                 mode = ADD_TYPE_NEXT;
172         else
173                 mode = ADD_TYPE_END;
174
175         m->mhandle->MediaGetList(LIST_TYPE_ALBUM_SONG,
176                 m->ctxtinfo->context, &list);
177         if (type ==  ACTION_TYPE_ADDTO) {
178                 if (!m->mhandle->MediaAddsongsPlaylist(lid, list))
179                         _ERR(" Adding songs to playlist failed ");
180                 else
181                         CCommonUI::CreateMsgBox(m->base, MUSIC_TEXT_ADDTO_MSG);
182
183                 return;
184         }
185
186         m->mhandle->UpdatePlaylist(list, mode);
187         sinfo = (CSongInfo *)eina_list_data_get(list);
188         eina_list_free(list);
189
190         if (type == ACTION_TYPE_PLAY) {
191                 m->mhandle->Stop();
192                 m->mhandle->SetCurrentSong(sinfo->Id());
193         }
194
195         CCommonUI::UpdatePlaybackView((EAddType)type, Layout(), m->focusedItem);
196 }
197
198
199 void CAlbumLayout::sm_CbCtxtClose(void *dt)
200 {
201         CAlbumLayout *root = (CAlbumLayout*)dt;
202
203         if (root)
204                 root->m_OnCtxtClose();
205 }
206
207
208 void CAlbumLayout::m_OnCtxtClose(void)
209 {
210         m->vmgr->PopView();
211         elm_object_item_focus_set(m->focusedItem, EINA_TRUE);
212 }
213
214
215 void CAlbumLayout::sm_CbItemSelect(void *data, Elm_Object_Item *it, const char *emission, const char *source)
216 {
217         CAlbumLayout *root = (CAlbumLayout*)data;
218
219         if (root)
220                 root->m_OnItemSelect(it, emission, source);
221 }
222
223
224 void CAlbumLayout::m_OnItemSelect(Elm_Object_Item *it, const char *emission, const char *source)
225 {
226         SAlbumItemInfo *itinfo;
227
228         itinfo = m_FindItemInfo(m->it_infolist, it);
229         if (!itinfo) {
230                 _ERR(" no item info found ");
231                 return;
232         }
233
234         m->layoutAlbumSongs->SetCurrentAlbum(itinfo->alinfo);
235         m_ShowAlbumSongs();
236 }
237
238
239 SAlbumItemInfo *CAlbumLayout::m_FindItemInfo(Eina_List *list, Elm_Object_Item *item)
240 {
241         Eina_List *l = NULL;
242         SAlbumItemInfo *itinfo = NULL;
243         void *obj = NULL;
244
245         if (!list || !item)
246                 return NULL;
247
248         EINA_LIST_FOREACH(list, l, obj) {
249                 itinfo = (SAlbumItemInfo *)obj;
250                 if (itinfo->item == item)
251                         return itinfo;
252         }
253
254         return NULL;
255 }
256
257
258 SAlbumItemInfo *CAlbumLayout::m_FindItemInfoById(Eina_List *list, int alid)
259 {
260         Eina_List *l = NULL;
261         SAlbumItemInfo *itinfo = NULL;
262         void *obj = NULL;
263         int id;
264
265         if (!list)
266                 return NULL;
267
268         EINA_LIST_FOREACH(list, l, obj) {
269                 itinfo = (SAlbumItemInfo *)obj;
270                 if (!itinfo->alinfo)
271                         continue;
272                 id = itinfo->alinfo->AlbumId();
273                 if (alid == id) {
274                         return itinfo;
275                 }
276         }
277
278         return NULL;
279 }
280
281
282 void CAlbumLayout::m_ShowAlbumSongs(void)
283 {
284         elm_object_part_content_unset(m->base, MUSIC_PART_CONTENT);
285
286         bool emptyStatus = m->layoutAlbumSongs->EmptyStatus();
287
288         if (emptyStatus == false) {
289                 elm_object_part_content_set(m->base, MUSIC_PART_CONTENT, m->layoutAlbumSongs->Layout());
290                 m->lmgr->Show(MUSIC_ALBUM_SONGS_LAYOUT);
291         }
292
293         if (m->callback.cbHandleEmptyStatus != NULL) {
294                 m->callback.cbHandleEmptyStatus(m->callback.cookie, emptyStatus);
295         }
296 }
297
298
299 void CAlbumLayout::m_RemoveAlbumList(Eina_List *list)
300 {
301         eina_list_free(list);
302 }
303
304
305 void CAlbumLayout::m_CreateAlbumGrid(void)
306 {
307         Evas_Object *grid;
308         Elm_Gengrid_Item_Class *grid_item;
309
310         grid = elm_gengrid_add(Layout());
311         if (!grid)
312                 return;
313
314         evas_object_size_hint_weight_set(grid,
315                 EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
316         elm_gengrid_align_set(grid, 0, 0);
317         elm_object_part_content_set(Layout(), MUSIC_PART_ALBUM_GRID, grid);
318         Connect(grid, ALBUM_GENGRID, TYPE_MOUSE_MOVE | TYPE_KEY_DOWN | TYPE_REALIZED | TYPE_UNREALIZED | TYPE_ACTIVATED);
319         elm_gengrid_item_size_set(grid,
320                 elm_config_scale_get() * GENGRID_ITEM_SIZE_W,
321                 elm_config_scale_get() * GENGRID_ITEM_SIZE_H);
322
323         m->grid = grid;
324
325         grid_item = elm_gengrid_item_class_new();
326         if (!grid_item) {
327                 evas_object_del(grid);
328                 _ERR(" elm_genlist_item_class_new failed ");
329                 return;
330         }
331
332         grid_item->item_style = MUSIC_STYLE_ALBUM_GRID;
333         grid_item->func.text_get = sm_CbGetGridItemText;
334         grid_item->func.content_get = sm_CbGetGridItemContent;
335         grid_item->func.state_get = NULL;
336         grid_item->func.del = sm_CbRemoveGridItem;
337         m->item_class = grid_item;
338
339         m_UpdateAlbumGrid(false);
340 }
341
342
343 void CAlbumLayout::m_EmptyAlbumGrid(bool sort_flag)
344 {
345         if (m->grid)
346                 elm_gengrid_clear(m->grid);
347
348         if (!sort_flag) {
349                 m_RemoveAlbumList(m->alblist);
350                 m->alblist = NULL;
351         }
352
353         eina_list_free(m->it_infolist);
354         m->it_infolist = NULL;
355 }
356
357
358 void CAlbumLayout::m_SortAlbumGrid(void)
359 {
360         int sortType;
361         const char *sortFuncId[] = {
362                 SORT_BY_NAME_AZ,
363                 SORT_BY_NAME_ZA,
364         };
365
366         sortType = CInfo::SortType();
367
368         m->alblist = CSortMgr::Sort(m->alblist, sortFuncId[sortType]);
369 }
370
371
372 void CAlbumLayout::m_UpdateAlbumGrid(bool sort_flag)
373 {
374         bool r;
375         CAlbumInfo *alinfo;
376         void *obj;
377         Eina_List *l;
378         SAlbumItemInfo *itinfo;
379         Elm_Object_Item *item;
380
381         /* Remove existing albumgrid and prepare afresh */
382         m_EmptyAlbumGrid(sort_flag);
383
384         if (!sort_flag) {
385                 r = m->mhandle->MediaGetList(LIST_TYPE_ALBUM, NULL, &(m->alblist));
386                 if (r == false ||
387                         eina_list_count(m->alblist) == 0) {
388                         _ERR(" Fetching album list from media failed ");
389                         SetEmptyStatus(true);
390                         return;
391                 }
392         }
393
394         m_SortAlbumGrid();
395
396         int i = 0;
397         EINA_LIST_FOREACH(m->alblist, l, obj) {
398                 alinfo = (CAlbumInfo *)obj;
399                 itinfo = (SAlbumItemInfo *)calloc(1, sizeof(*itinfo));
400                 if (!itinfo)
401                         return;
402
403                 itinfo->alinfo = alinfo;
404                 item = elm_gengrid_item_append(m->grid, m->item_class, itinfo, NULL, NULL);
405                 itinfo->item = item;
406                 m->it_infolist = eina_list_append(m->it_infolist, itinfo);
407                 if (i == 0)
408                         m->focusedItem = item;
409                 i++;
410         }
411 }
412
413
414 bool CAlbumLayout::Create(CLayoutMgr *mgr, const char *albumId)
415 {
416         ASSERT(!m);
417         ASSERT(mgr);
418
419         _CREATE_BEGIN{
420                 Evas_Object *base = NULL;
421                 Evas_Object *win = NULL;
422                 Evas_Object *layout = NULL;
423                 CMusicController *mhandle = NULL;
424                 CViewMgr *vmgr = NULL;
425                 CAlbumSongsLayout *layoutAlbumSongs = NULL;
426                 SParcel parcel;
427
428                 _CHECK(m = new SAlbumLayout)
429                 _CHECK(vmgr = CViewMgr::GetInstance())
430                 _CHECK(base = mgr->Base())
431                 _CHECK(win = vmgr->Window())
432                 _CHECK(mhandle = CMusicController::GetInstance())
433                 _CHECK(layout = CCommonUI::AddBase(base, MUSIC_ALBUM_LAYOUT))
434                 _CHECK(CExtBaseLayout::Create(layout))
435                 _CHECK(layoutAlbumSongs = new CAlbumSongsLayout(MUSIC_ALBUM_SONGS_LAYOUT))
436                 _CHECK(layoutAlbumSongs->Create(mgr))
437                 _CHECK(mgr->AddLayout(layoutAlbumSongs))
438
439                 _WHEN_SUCCESS{
440                         if (albumId) {
441                                 m->album_id = new char[strlen(albumId) + 1];
442                                 strcpy(m->album_id, albumId);
443                         }
444                         else
445                                 m->album_id = NULL;
446
447                         m->win = win;
448                         m->base = base;
449                         m->vmgr = vmgr;
450                         m->mhandle = mhandle;
451                         m->lmgr = mgr;
452                         m->callback.cbHandleEmptyStatus = NULL;
453                         m->callback.cookie = NULL;
454                         m->layoutAlbumSongs = layoutAlbumSongs;
455
456                         m_CreateAlbumGrid();
457                         Connect(layout, ALBUM_LAYOUT, TYPE_KEY_DOWN);
458
459                         parcel.ctxtInfo = NULL;
460                         parcel.updateType = E_LAYOUT_UPDATE;
461                         parcel.layoutId = MUSIC_ALBUM_LAYOUT;
462                         parcel.keyEvent = NULL;
463                         m->vmgr->UpdateView((const char *)MUSIC_BASE_VIEW, &parcel);
464                 }
465
466                 _CHECK_FAIL{ mgr->RemoveLayout(layoutAlbumSongs); }
467                 _CHECK_FAIL{ m->layoutAlbumSongs->Destroy(); }
468                 _CHECK_FAIL{ delete m->layoutAlbumSongs; }
469                 _CHECK_FAIL{ CExtBaseLayout::Destroy(); }
470                 _CHECK_FAIL{ evas_object_del(layout); }
471                 _CHECK_FAIL{}
472                 _CHECK_FAIL{}
473                 _CHECK_FAIL{}
474                 _CHECK_FAIL{}
475                 _CHECK_FAIL{ delete m; m = NULL; }
476         } _CREATE_END_AND_CATCH{ return false; }
477
478         return true;
479 }
480
481 void CAlbumLayout::Destroy(void)
482 {
483         ASSERT(m);
484
485         Disconnect(Layout());
486
487         m->lmgr->RemoveLayout(m->layoutAlbumSongs);
488         m->layoutAlbumSongs->Destroy();
489         delete m->layoutAlbumSongs;
490
491         m_EmptyAlbumGrid(false);
492         CExtBaseLayout::Destroy();
493         evas_object_del(Layout());
494
495         free(m->ctxtinfo);
496         delete[] m->album_id;
497         delete m;
498         m = NULL;
499 }
500
501
502 void CAlbumLayout::t_OnShow(void)
503 {
504         ASSERT(m);
505
506         SAlbumItemInfo *itinfo = NULL;
507
508         m_UpdateAlbumGrid(false);
509
510         SParcel parcel;
511         memset(&parcel, 0, sizeof(SParcel));
512         parcel.updateType = E_LAYOUT_UPDATE;
513         parcel.layoutId = MUSIC_ALBUM_LAYOUT;
514         m->vmgr->UpdateView((const char *)MUSIC_BASE_VIEW, &parcel);
515
516         evas_object_show(Layout());
517
518         if (m->album_id) {
519                 itinfo = m_FindItemInfoById(m->it_infolist,
520                         atoi(m->album_id));
521                 if (itinfo) {
522                         m->layoutAlbumSongs->SetCurrentAlbum(itinfo->alinfo);
523                         m_ShowAlbumSongs();
524                         return;
525                 }
526                 delete[] m->album_id;
527                 m->album_id = NULL;
528         }
529 }
530
531
532 void CAlbumLayout::Update(bool focusFlag)
533 {
534         ASSERT(m);
535
536         CAlbumInfo *alinfo = NULL;
537         SAlbumItemInfo *itinfo = NULL;
538         int id;
539
540         if (!focusFlag) {
541                 m_UpdateAlbumGrid(true);
542                 return;
543         }
544
545         alinfo = m->layoutAlbumSongs->CurrentAlbum();
546         if (alinfo) {
547                 id = alinfo->AlbumId();
548                 itinfo = m_FindItemInfoById(m->it_infolist, id);
549                 if (itinfo) {
550                         m->layoutAlbumSongs->SetCurrentAlbum(NULL);
551
552                         elm_gengrid_item_show(itinfo->item,
553                                 ELM_GENGRID_ITEM_SCROLLTO_IN);
554                         elm_object_item_focus_set(itinfo->item, EINA_TRUE);
555                         return;
556                 }
557         }
558
559         elm_object_focus_set(m->grid, EINA_TRUE);
560 }
561
562
563 void CAlbumLayout::SetFocus(const char *btnStr)
564 {
565         ASSERT(m);
566         ASSERT(m->focusedItem);
567
568         elm_gengrid_item_show(m->focusedItem, ELM_GENGRID_ITEM_SCROLLTO_IN);
569         elm_object_item_focus_set(m->focusedItem, EINA_TRUE);
570 }
571
572
573 void CAlbumLayout::SetEmptyStatusHandleCallback(void(*handleEmptyStatusCb)(void *cookie, bool emptyStatus), void *cookie)
574 {
575         ASSERT(m);
576
577         m->callback.cbHandleEmptyStatus = handleEmptyStatusCb;
578         m->callback.cookie = cookie;
579 }
580
581
582 void CAlbumLayout::OnKeyDown(int id, Evas *e, Evas_Object *obj, Evas_Event_Key_Down *ev)
583 {
584         switch (id) {
585         case ALBUM_LAYOUT:
586                 if (!strcmp(ev->keyname, KEY_BACK) ||
587                         !strcmp(ev->keyname, KEY_BACK_REMOTE)) {
588                         SParcel parcel;
589                         memset(&parcel, 0, sizeof(SParcel));
590                         parcel.updateType = E_FOCUS_UPDATE;
591                         m->vmgr->UpdateView((const char *)MUSIC_BASE_VIEW, &parcel);
592                 }
593                 break;
594
595         case ALBUM_GENGRID:
596                 {
597                         Elm_Object_Item *it = NULL;
598                         SContentInfo *ctxtinfo = NULL;
599                         SAlbumItemInfo *itinfo = NULL;
600
601                         it = elm_object_focused_item_get(obj);
602                         if (!it) {
603                                 _ERR(" unable to get focused item ");
604                                 return;
605                         }
606
607                         m->focusedItem = it;
608
609                         if (!strcmp(ev->keyname, KEY_MENU) ||
610                                 !strcmp(ev->keyname, KEY_MENU_REMOTE)) {
611                                 if (m->ctxtinfo) {
612                                         free(m->ctxtinfo);
613                                         m->ctxtinfo = NULL;
614                                 }
615
616                                 ctxtinfo = (SContentInfo *)calloc(1, sizeof(*ctxtinfo));
617                                 if (!ctxtinfo)
618                                         return;
619
620                                 itinfo = m_FindItemInfo(m->it_infolist, it);
621                                 if (!itinfo) {
622                                         free(ctxtinfo);
623                                         return;
624                                 }
625
626                                 ctxtinfo->type = CONTEXT_TYPE_ALBUM;
627                                 ctxtinfo->context = itinfo->alinfo;
628                                 ctxtinfo->cbdata = this;
629                                 ctxtinfo->update = sm_CbCtxtUpdate;
630                                 ctxtinfo->close = sm_CbCtxtClose;
631
632                                 m->ctxtinfo = ctxtinfo;
633
634                                 SParcel parcel;
635                                 memset(&parcel, 0, sizeof(SParcel));
636                                 parcel.ctxtInfo = ctxtinfo;
637                                 if (!m->vmgr->PushView(MUSIC_CONTEXT_VIEW, &parcel))
638                                         _ERR("viewmgr push view MUSIC_CONTEXT_VIEW failed");
639                         }
640                 }
641                 break;
642
643         default:
644                 break;
645         }
646 }
647
648
649 void CAlbumLayout::OnMouseMove(int id, Evas *e, Evas_Object *obj, Evas_Event_Mouse_Move *ev)
650 {
651         switch (id) {
652         case ALBUM_GENGRID:
653                 {
654                         Elm_Object_Item *item;
655
656                         item = elm_gengrid_at_xy_item_get(obj, ev->cur.canvas.x,
657                                 ev->cur.canvas.y, NULL, NULL);
658                         if (!item)
659                                 return;
660
661                         if (!elm_object_item_focus_get(item))
662                                 elm_object_item_focus_set(item, EINA_TRUE);
663
664                         m->focusedItem = item;
665                 }
666                 break;
667
668         default:
669                 break;
670         }
671 }
672
673
674 void CAlbumLayout::OnRealized(int id, Evas_Object *obj, Elm_Object_Item *item)
675 {
676         switch (id) {
677         case ALBUM_GENGRID:
678                 {
679                         elm_object_item_signal_callback_add(item,
680                         MUSIC_SIGNAL_BTN_CLICKED, MUSIC_BASE_VIEW,
681                         sm_CbItemSelect, this);
682                 }
683                 break;
684
685         default:
686                 break;
687         }
688 }
689
690
691 void CAlbumLayout::OnUnrealized(int id, Evas_Object *obj, Elm_Object_Item *item)
692 {
693         switch (id) {
694         case ALBUM_GENGRID:
695                 {
696                         elm_object_item_signal_callback_del(item,
697                         MUSIC_SIGNAL_BTN_CLICKED, MUSIC_BASE_VIEW,
698                         sm_CbItemSelect);
699                 }
700                 break;
701
702         default:
703                 break;
704         }
705 }
706
707
708 void CAlbumLayout::OnActivated(int id, Evas_Object *obj, Elm_Object_Item *item)
709 {
710         switch (id) {
711         case ALBUM_GENGRID:
712                 m_OnItemSelect(item, NULL, NULL);
713                 break;
714
715         default:
716                 break;
717         }
718 }