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