root/applications/osmo4_w32/Playlist.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. DoDataExchange
  2. BEGIN_MESSAGE_MAP
  3. OnSize
  4. OnDropFiles
  5. OnNotify
  6. Clear
  7. ClearButPlaying
  8. UpdateEntry
  9. RefreshList
  10. OnPlAddFile
  11. OnClose
  12. OnPlRemFile
  13. OnSelUp
  14. OnSelDown
  15. OnPlRemAll
  16. OnPlRemDead
  17. LocCbck
  18. pl_enum_dir_item
  19. pl_enum_dir_dirs
  20. AddDir
  21. OnPlAddDir
  22. OnPlAddDirRec
  23. OnPlAddUrl
  24. OnPlSave
  25. Save
  26. OnPlOpen
  27. OpenPlayList
  28. OnRclickFilelist
  29. OnReverseSelection
  30. OnReverseList
  31. OnRandomize
  32. Sort
  33. OnSortFile
  34. OnSortTitle
  35. OnSortDuration
  36. HasValidEntries
  37. RefreshCurrent
  38. OnDblclkFilelist
  39. OnPlPlay
  40. Truncate
  41. QueueURL
  42. PlayNext
  43. PlayPrev
  44. SetDead
  45. SetDuration
  46. GetDisplayName
  47. GetURL

// Playlist.cpp : implementation file
//

#include "stdafx.h"
#include "osmo4.h"
#include "MainFrm.h"
#include "OpenURL.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Playlist dialog

PLEntry::PLEntry(CString url, char *path)
{
        if (!path || strrchr(url, '\\') || strstr(url, "://")) {
                m_url = gf_strdup(url);
        } else {
                char szPath[MAX_PATH];
                strcpy(szPath, path);
                strcat(szPath, url);
                m_url = gf_strdup(szPath);
        }
        char *str = (char *) strrchr(url, '\\');
        if (!str) str = (char *) strrchr(url, '/');
        if (str && strlen(str+1)) {
                m_disp_name = gf_strdup(str+1);
                str = strrchr(m_disp_name, '.');
                if (str) str[0] = 0;
        } else {
                str = (char *) strstr(url, "://");
                if (str) {
                        str += 3;
                        m_disp_name = gf_strdup(str);
                } else {
                        m_disp_name = gf_strdup(url);
                        str = strrchr(m_disp_name, '.');
                        if (str) str[0] = 0;
                }
        }
        m_duration = 0;
        m_bIsDead = GF_FALSE;
        m_bIsPlaying = GF_FALSE;
        m_bIsSelected = GF_FALSE;
}

PLEntry::~PLEntry()
{
        if (m_url) gf_free(m_url);
        if (m_disp_name) gf_free(m_disp_name);

}


static char szCacheDir[MAX_PATH];

Playlist::Playlist()
        : CDialog(Playlist::IDD, NULL)
{
        //{{AFX_DATA_INIT(Playlist)
        //}}AFX_DATA_INIT

        m_entries = gf_list_new();
        m_cur_entry = -1;
        m_all_dead_entries=-1;
        GetCurrentDirectory(MAX_PATH, szCacheDir);
}

Playlist::~Playlist()
{
        Clear();
        gf_list_del(m_entries);
}

void Playlist::DoDataExchange(CDataExchange* pDX)
{
        CDialog::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(Playlist)
        DDX_Control(pDX, IDC_FILELIST, m_FileList);
        //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(Playlist, CDialog)
        //{{AFX_MSG_MAP(Playlist)
        ON_WM_SIZE()
        ON_COMMAND(ID_PL_ADD_FILE, OnPlAddFile)
        ON_COMMAND(ID_PL_REM_FILE, OnPlRemFile)
        ON_COMMAND(ID_PL_UP, OnSelUp)
        ON_COMMAND(ID_PL_DOWN, OnSelDown)
        ON_COMMAND(ID_PL_SAVE, OnPlSave)
        ON_WM_DROPFILES()
        ON_WM_CLOSE()
        ON_WM_DESTROY()
        ON_COMMAND(ID_PL_REM_DEAD, OnPlRemDead)
        ON_COMMAND(ID_PL_REM_ALL, OnPlRemAll)
        ON_COMMAND(ID_PL_ADD_DIR, OnPlAddDir)
        ON_COMMAND(ID_PL_ADD_DIR_REC, OnPlAddDirRec)
        ON_COMMAND(ID_PL_ADD_URL, OnPlAddUrl)
        ON_COMMAND(ID_PL_OPEN, OnPlOpen)
        ON_COMMAND(ID_PL_PLAY, OnPlPlay)
        ON_COMMAND(ID_PL_SEL_REV, OnReverseSelection)
        ON_COMMAND(ID_PL_SORT_REV, OnReverseList)
        ON_COMMAND(ID_PL_RANDOM, OnRandomize)
        ON_COMMAND(ID_PL_SORT_TITLE, OnSortTitle)
        ON_COMMAND(ID_PL_SORT_FILE, OnSortFile)
        ON_COMMAND(ID_PL_SORT_DUR, OnSortDuration)
        ON_NOTIFY(NM_RCLICK, IDC_FILELIST, OnRclickFilelist)
        ON_NOTIFY(NM_DBLCLK, IDC_FILELIST, OnDblclkFilelist)
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// Playlist message handlers

BOOL Playlist::OnInitDialog()
{
        UINT buttonArray[50];
        TBBUTTONINFO bi;
        u32 *ba;
        CDialog::OnInitDialog();


        SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), TRUE);
        SetIcon(AfxGetApp()->LoadIcon(IDR_MAINFRAME), FALSE);

        if (!m_toolBar.CreateEx(this, WS_CHILD | CBRS_TOP | CBRS_FLYBY) ||
                !m_toolBar.LoadBitmap(IDR_PLAYLIST))
        {
                TRACE0("Failed to create toolbar\n");
                return 0;
        }

        ba = &buttonArray[0];
        *ba = ID_PL_OPEN;
        ba++;
        *ba = ID_PL_SAVE;
        ba++;
        *ba = ID_SEPARATOR;
        ba++;
        *ba = ID_PL_ADD_FILE;
        ba++;
        *ba = ID_PL_REM_FILE;
        ba++;
        *ba = ID_SEPARATOR;
        ba++;
        *ba = ID_PL_UP;
        ba++;
        *ba = ID_PL_DOWN;
        ba++;
        *ba = ID_SEPARATOR;
        ba++;
        *ba = ID_PL_SORT_FILE;
        ba++;
        m_toolBar.SetButtons(buttonArray, 9);
        m_toolBar.SetButtonInfo(0, ID_PL_OPEN, TBBS_BUTTON, 0);
        m_toolBar.SetButtonInfo(1, ID_PL_SAVE, TBBS_BUTTON, 1);
        m_toolBar.SetButtonInfo(2, ID_SEPARATOR, TBBS_SEPARATOR, 0);
        m_toolBar.SetButtonInfo(3, ID_PL_ADD_FILE, TBBS_DROPDOWN | TBBS_BUTTON, 2);
        m_toolBar.SetButtonInfo(4, ID_PL_REM_FILE, TBBS_DROPDOWN | TBBS_BUTTON, 3);
        m_toolBar.SetButtonInfo(5, ID_SEPARATOR, TBBS_SEPARATOR, 0);
        m_toolBar.SetButtonInfo(6, ID_PL_UP, TBBS_BUTTON, 4);
        m_toolBar.SetButtonInfo(7, ID_PL_DOWN, TBBS_BUTTON, 5);
//      m_toolBar.SetButtonInfo(8, ID_SEPARATOR, TBBS_SEPARATOR, 0);
        m_toolBar.SetButtonInfo(8, ID_PL_SORT_FILE, TBBS_DROPDOWN | TBBS_BUTTON, 6);

        CToolBarCtrl &ctrl = m_toolBar.GetToolBarCtrl();
        ctrl.SetStyle(TBSTYLE_FLAT | TBSTYLE_DROPDOWN);
        ctrl.SetExtendedStyle(TBSTYLE_EX_DRAWDDARROWS);

        memset(&bi, 0, sizeof(bi));
        bi.cbSize = sizeof(bi);
        ctrl.GetButtonInfo(3, &bi);
        bi.fsStyle |= TBSTYLE_DROPDOWN;
        ctrl.SetButtonInfo(ID_PL_ADD_FILE, &bi);

        memset(&bi, 0, sizeof(bi));
        bi.cbSize = sizeof(bi);
        ctrl.GetButtonInfo(4, &bi);
        bi.fsStyle |= TBBS_DROPDOWN | TBSTYLE_DROPDOWN;
        ctrl.SetButtonInfo(ID_PL_REM_FILE, &bi);

        memset(&bi, 0, sizeof(bi));
        bi.cbSize = sizeof(bi);
        ctrl.GetButtonInfo(9, &bi);
        bi.fsStyle |= TBSTYLE_DROPDOWN;
        ctrl.SetButtonInfo(ID_PL_SORT_FILE, &bi);

        m_FileList.InsertColumn(0, "", LVCFMT_LEFT, 30, 0);
        m_FileList.InsertColumn(1, "Title", LVCFMT_LEFT, 200, 1);
        m_FileList.InsertColumn(2, "Duration", LVCFMT_LEFT, 200, 2);


        m_toolBar.UpdateWindow();
        m_toolBar.ShowWindow(SW_SHOW);

        SetWindowPos(NULL, 0, 0, 400, 600, SWP_NOZORDER | SWP_NOMOVE);

        PostMessage(WM_NULL);
        DragAcceptFiles();

        return TRUE;
}

void Playlist::OnSize(UINT nType, int cx, int cy)
{
        u32 tool_h;
        CDialog::OnSize(nType, cx, cy);
        RECT rc;
        if (!m_toolBar.m_hWnd) return;
        if (!m_FileList.m_hWnd) return;

        m_toolBar.GetClientRect(&rc);
        tool_h = rc.bottom - rc.top;
        m_toolBar.SetWindowPos(this, 0, 0, cx, tool_h, SWP_NOZORDER);
        m_FileList.SetWindowPos(this, 0, tool_h, cx, cy-tool_h, SWP_NOZORDER);

        m_FileList.SetExtendedStyle(m_FileList.GetExtendedStyle() | LVS_EX_FULLROWSELECT);

        m_FileList.SetColumnWidth(0, 30);
        m_FileList.SetColumnWidth(2, 60);
        m_FileList.SetColumnWidth(1, cx-95);

}

void Playlist::OnDropFiles(HDROP hDropInfo)
{
        u32 i, count;
        Osmo4 *app = GetApp();
        char fileName[MAX_PATH];
        count = ::DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
        if (!count) return;
        for (i=0; i<count; i++) {
                ::DragQueryFile (hDropInfo, i, fileName, MAX_PATH);
                QueueURL(fileName);
        }
        RefreshList();
}


BOOL Playlist::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{

        if (((LPNMHDR)lParam)->code == TBN_DROPDOWN) {
                RECT rc;
                POINT pt;
                CMenu *pPopup = new CMenu();
                pPopup->CreatePopupMenu();

                m_toolBar.GetWindowRect(&rc);
                pt.y = rc.bottom;
                pt.x = rc.left;

                if ( ((LPNMTOOLBAR)lParam)->iItem == ID_PL_ADD_FILE) {
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_ADD_DIR, "Directory...");
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_ADD_DIR_REC, "Directory and subfolders...");
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_ADD_URL, "URL...");
                        m_toolBar.GetToolBarCtrl().GetItemRect(3, &rc);
                        pt.x += rc.left;
                } else if ( ((LPNMTOOLBAR)lParam)->iItem == ID_PL_REM_FILE) {
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_REM_ALL, "Clear");
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_REM_DEAD, "Remove dead entries");

                        m_toolBar.GetToolBarCtrl().GetItemRect(4, &rc);
                        pt.x += rc.left;
                } else if ( ((LPNMTOOLBAR)lParam)->iItem == ID_PL_SORT_FILE) {
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_TITLE, "Sort Files by title");
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_FILE, "Sort Files by filename");
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_DUR, "Sort Files by duration");
                        pPopup->AppendMenu(MF_SEPARATOR,0);
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_REV, "Reverse Playlist");
                        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_RANDOM, "Shuffle Playlist");

                        m_toolBar.GetToolBarCtrl().GetItemRect(8, &rc);
                        pt.x += rc.left;
                }
                pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
                delete pPopup;

                return FALSE;
        }
        return CDialog::OnNotify(wParam, lParam, pResult);
}

void Playlist::Clear()
{
        m_FileList.DeleteAllItems();
        while (gf_list_count(m_entries)) {
                PLEntry *ple = (PLEntry *) gf_list_get(m_entries, 0);
                gf_list_rem(m_entries, 0);
                delete ple;
        }
        m_cur_entry = -1;
}

void Playlist::ClearButPlaying()
{
        PLEntry *p=NULL;
        if (m_cur_entry>=0) p = (PLEntry *) gf_list_get(m_entries, m_cur_entry);
        if (p) gf_list_rem(m_entries, m_cur_entry);

        Clear();
        if (p) {
                gf_list_add(m_entries, p);
                m_cur_entry = 0;
        }
        RefreshList();
}

void Playlist::UpdateEntry(u32 i)
{
        char szText[20];
        PLEntry *ple = (PLEntry *) gf_list_get(m_entries, i);
        if (i+1<10) sprintf(szText, "00%d", i+1);
        else if (i+1<100) sprintf(szText, "0%d", i+1);
        else sprintf(szText, "%d", i+1);
        m_FileList.SetItem(i, 0, LVIF_TEXT, szText, 0, 0, 0, 0);

        CString str;
        if (ple->m_bIsDead) {
                str = "!! DEAD !! ";
                str += ple->m_disp_name;
        }
        else if (ple->m_bIsPlaying) {
                str = ">> ";
                str += ple->m_disp_name;
                str += " >>";
        }
        else str = ple->m_disp_name;
        m_FileList.SetItem(i, 1, LVIF_TEXT, str, 0, 0, 0, 0);

        if (ple->m_duration) {
                u32 h = (u32) (ple->m_duration / 3600);
                u32 m = (u32) (ple->m_duration / 60) - h*60;
                u32 s = (u32) (ple->m_duration) - h*3600 - m*60;
                sprintf(szText, "%02d:%02d:%02d", h, m, s);
                m_FileList.SetItem(i, 2, LVIF_TEXT, szText, 0, 0, 0, 0);
        } else {
                m_FileList.SetItem(i, 2, LVIF_TEXT, "Unknown", 0, 0, 0, 0);
        }

}

void Playlist::RefreshList()
{
        u32 i, top_idx;
        char szPath[GF_MAX_PATH];

        top_idx = m_FileList.GetTopIndex();
        m_FileList.DeleteAllItems();

        for (i=0; i<gf_list_count(m_entries); i++) {
                PLEntry *ple = (PLEntry *) gf_list_get(m_entries, i);
                m_FileList.InsertItem(i, "");
                m_FileList.SetItemData(i, (u32) ple);

                UpdateEntry(i);
                /*in case of sorting*/
                if (ple->m_bIsPlaying) m_cur_entry = i;

                if (ple->m_bIsSelected) {
                        m_FileList.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
                        ple->m_bIsSelected = GF_FALSE;
                }
        }

        if (m_cur_entry >= (s32)gf_list_count(m_entries)) m_cur_entry = gf_list_count(m_entries);
        else {
                s32 last_idx = top_idx + m_FileList.GetCountPerPage();
                m_FileList.EnsureVisible(top_idx, 0);
                if (gf_list_count(m_entries)<(u32) last_idx) last_idx = gf_list_count(m_entries);
                m_FileList.EnsureVisible(last_idx, 1);
        }


        strcpy((char *) szPath, GetApp()->szUserPath);
        strcat(szPath, "gpac_pl.m3u");
        Save(szPath, GF_TRUE);
}

void Playlist::OnPlAddFile()
{
        Osmo4 *app = GetApp();
        CString sFiles = app->GetFileFilter();

        CFileDialog fd(TRUE,NULL,NULL, OFN_ALLOWMULTISELECT | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST , sFiles);
        fd.m_ofn.nMaxFile = 25000;
        fd.m_ofn.lpstrFile = (char *) gf_malloc(sizeof(char) * fd.m_ofn.nMaxFile);
        fd.m_ofn.lpstrFile[0] = 0;

        if (fd.DoModal() == IDOK) {
                s32 cur = m_FileList.GetItemCount();
                POSITION pos = fd.GetStartPosition();
                while (pos) {
                        QueueURL(fd.GetNextPathName(pos));
                }
        }
        gf_free(fd.m_ofn.lpstrFile);
        m_all_dead_entries=-1;
        RefreshList();
}

void Playlist::OnClose()
{
        ShowWindow(SW_HIDE);
}

void Playlist::OnPlRemFile()
{
        if (!m_FileList.GetSelectedCount()) return;
        POSITION pos = m_FileList.GetFirstSelectedItemPosition();
        while (pos != NULL) {
                int nItem = m_FileList.GetNextSelectedItem(pos);
                PLEntry *ple = (PLEntry *) m_FileList.GetItemData(nItem);
                gf_list_del_item(m_entries, ple);
                delete ple;
        }
        m_all_dead_entries=-1;
        RefreshList();
}

void Playlist::OnSelUp()
{
        s32 i;
        if (!m_FileList.GetSelectedCount()) return;
        POSITION pos = m_FileList.GetFirstSelectedItemPosition();
        int nItem = m_FileList.GetNextSelectedItem(pos);
        if (nItem==0) return;

        pos = m_FileList.GetFirstSelectedItemPosition();
        while (pos != NULL) {
                nItem = m_FileList.GetNextSelectedItem(pos);
                PLEntry *ple = (PLEntry *) m_FileList.GetItemData(nItem);
                i = gf_list_del_item(m_entries, ple);
                assert(i>=1);
                gf_list_insert(m_entries, ple, i-1);
                ple->m_bIsSelected = GF_TRUE;
        }
        RefreshList();
}

void Playlist::OnSelDown()
{
        s32 i, nItem;
        if (!m_FileList.GetSelectedCount()) return;
        POSITION pos = m_FileList.GetFirstSelectedItemPosition();
        while (pos != NULL) nItem = m_FileList.GetNextSelectedItem(pos);

        if ((u32) nItem + 1 == gf_list_count(m_entries)) return;

        pos = m_FileList.GetFirstSelectedItemPosition();
        while (pos != NULL) {
                nItem = m_FileList.GetNextSelectedItem(pos);
                PLEntry *ple = (PLEntry *) m_FileList.GetItemData(nItem);
                i = gf_list_del_item(m_entries, ple);
                gf_list_insert(m_entries, ple, i+1);
                ple->m_bIsSelected = GF_TRUE;
        }
        RefreshList();
}

void Playlist::OnPlRemAll()
{
        Clear();
        RefreshList();
        m_cur_entry = -1;
}

void Playlist::OnPlRemDead()
{
        for (u32 i=0; i<gf_list_count(m_entries); i++) {
                PLEntry *ple = (PLEntry *) gf_list_get(m_entries, i);
                if (!ple->m_bIsDead) continue;
                gf_list_rem(m_entries, i);
                i--;
                delete ple;
        }
        m_all_dead_entries=-1;
        RefreshList();
}


static int CALLBACK LocCbck(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData)
{
        char dir[MAX_PATH];
        if (uMsg == BFFM_INITIALIZED) {
                strcpy(dir, szCacheDir);
                SendMessage(hwnd, BFFM_SETSELECTION, TRUE,(LPARAM) dir);
        }
        return 0;
}
static Bool pl_enum_dir_item(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
{
        Osmo4 *gpac = GetApp();
        Playlist *_this = (Playlist *)cbck;

        if (gf_term_is_supported_url(gpac->m_term, item_name, GF_FALSE, GF_TRUE)) {
                _this->QueueURL(item_path);
        }
        return GF_FALSE;
}

static Bool pl_enum_dir_dirs(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
{
        gf_enum_directory(item_path, GF_FALSE, pl_enum_dir_item, cbck, NULL);
        gf_enum_directory(item_path, GF_TRUE, pl_enum_dir_dirs, cbck, NULL);
        return GF_FALSE;
}


void Playlist::AddDir(Bool do_recurse)
{
        BROWSEINFO brw;
        LPMALLOC pMalloc;
        LPITEMIDLIST ret;
        char dir[MAX_PATH];

        Bool res = GF_FALSE;
        if (NOERROR == ::SHGetMalloc(&pMalloc) ) {
                memset(&brw, 0, sizeof(BROWSEINFO));
                brw.hwndOwner = this->GetSafeHwnd();
                brw.pszDisplayName = dir;
                brw.lpszTitle = "Select Directory...";
                brw.ulFlags = 0L;
                brw.lpfn = LocCbck;

                ret = SHBrowseForFolder(&brw);
                if (ret != NULL) {
                        if (::SHGetPathFromIDList(ret, dir)) res = GF_TRUE;
                        pMalloc->Free(ret);
                }
                pMalloc->Release();
        }
        if (!res) return;
        strcpy(szCacheDir, dir);

        gf_enum_directory(dir, GF_FALSE, pl_enum_dir_item, this, NULL);
        if (do_recurse) gf_enum_directory(dir, GF_FALSE, pl_enum_dir_dirs, this, NULL);
        m_all_dead_entries=-1;
        RefreshList();
}
void Playlist::OnPlAddDir()
{
        AddDir(GF_FALSE);
}
void Playlist::OnPlAddDirRec()
{
        AddDir(GF_TRUE);
}

void Playlist::OnPlAddUrl()
{
        COpenUrl url;
        if (url.DoModal() != IDOK) return;
        PLEntry *ple = new PLEntry(url.m_url);
        gf_list_add(m_entries, ple);
        m_all_dead_entries=-1;
        RefreshList();
}

void Playlist::OnPlSave()
{
        Bool save_m3u;
        char szPath[GF_MAX_PATH];
        if (!gf_list_count(m_entries)) return;
        CFileDialog fd(FALSE,NULL,NULL, OFN_OVERWRITEPROMPT, "M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|");
        if (fd.DoModal() != IDOK) return;

        strcpy(szPath, fd.GetPathName());
        strlwr(szPath);
        save_m3u = (fd.m_ofn.nFilterIndex==1) ? GF_TRUE : GF_FALSE;
        if (save_m3u) {
                if (!strstr(szPath, ".m3u")) {
                        strcpy(szPath, fd.GetPathName());
                        strcat(szPath, ".m3u");
                } else {
                        strcpy(szPath, fd.GetPathName());
                }
        } else {
                if (!strstr(szPath, ".pls")) {
                        strcpy(szPath, fd.GetPathName());
                        strcat(szPath, ".pls");
                } else {
                        strcpy(szPath, fd.GetPathName());
                }
        }
        Save(szPath, save_m3u);
}

void Playlist::Save(char *szPath, Bool save_m3u)
{
        FILE *out = gf_fopen(szPath, "wt");
        if (!save_m3u)
                fprintf(out, "[playlist]\nNumberOfEntries=%d\n", gf_list_count(m_entries));

        for (u32 i=0; i<gf_list_count(m_entries); i++) {
                PLEntry *ple = (PLEntry *) gf_list_get(m_entries, i);
                if (save_m3u) {
                        fprintf(out, "%s\n", ple->m_url);
                } else {
                        fprintf(out, "File%d=%s\n", i+1, ple->m_url);
                        fprintf(out, "Title%d=%s\n", i+1, ple->m_disp_name);
                        fprintf(out, "Length%d=%d\n", i+1, ple->m_duration ? ple->m_duration : -1);

                }
        }
        if (!save_m3u) fprintf(out, "Version=2\n");

        fprintf(out, "\n");
        gf_fclose(out);
}

void Playlist::OnPlOpen()
{
        CFileDialog fd(TRUE,NULL,NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, "M3U & PLS Playlists|*.m3u;*.pls|M3U Playlists|*.m3u|ShoutCast Playlists|*.pls|");
        if (fd.DoModal() != IDOK) return;

        Clear();
        OpenPlayList(fd.GetPathName());
        m_cur_entry = 0;
        Play();
}

void Playlist::OpenPlayList(CString fileName)
{
        FILE *pl;
        PLEntry *ple;
        Bool load_m3u, go;
        char szLine[GF_MAX_PATH], *sep;
        char szPath[GF_MAX_PATH];

        strcpy(szPath, fileName);
        sep = strrchr(szPath, '\\');
        if (sep) sep[1] = 0;
        else szPath[0] = 0;

        pl = gf_fopen(fileName, "rt");
        if (!pl) return;
        ple = NULL;
        load_m3u = GF_TRUE;
        while (!feof(pl)) {
                fgets(szLine, GF_MAX_PATH, pl);
                go = GF_TRUE;
                while (go) {
                        switch (szLine[strlen(szLine)-1]) {
                        case '\n':
                        case '\r':
                        case ' ':
                                szLine[strlen(szLine)-1] = 0;
                                break;
                        default:
                                go = GF_FALSE;
                                break;
                        }
                }
                if (!strlen(szLine)) continue;
                if (!stricmp(szLine, "[playlist]")) {
                        load_m3u = GF_FALSE;
                } else if (load_m3u) {
                        ple = new PLEntry(szLine, szPath);
                        gf_list_add(m_entries, ple);
                } else if (!strnicmp(szLine, "file", 4)) {
                        char *st = strchr(szLine, '=');
                        if (!st) ple = NULL;
                        else {
                                ple = new PLEntry(st + 1, szPath);
                                gf_list_add(m_entries, ple);
                        }
                } else if (ple && !strnicmp(szLine, "Length", 6)) {
                        char *st = strchr(szLine, '=');
                        s32 d = atoi(st + 1);
                        if (d>0) ple->m_duration = d;
                } else if (ple && !strnicmp(szLine, "Title", 5)) {
                        char *st = strchr(szLine, '=');
                        gf_free(ple->m_disp_name);
                        ple->m_disp_name = gf_strdup(st + 6);
                }
        }
        gf_fclose(pl);
        m_all_dead_entries=-1;
        m_cur_entry = -1;
        RefreshList();
}


void Playlist::OnRclickFilelist(NMHDR* pNMHDR, LRESULT* pResult)
{
        if (!m_FileList.GetItemCount()) return;

        CMenu *pPopup = new CMenu();
        pPopup->CreatePopupMenu();

        if (m_FileList.GetSelectedCount()==1) {
                pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_PLAY, "Play");
                pPopup->AppendMenu(MF_SEPARATOR, 0, "");
        }
        pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SEL_REV, "Inverse Selection");
        if (m_FileList.GetSelectedCount()) pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_REM_FILE, "Remove File(s)");
        if (m_FileList.GetItemCount()>1) {
                pPopup->AppendMenu(MF_SEPARATOR, 0, "");
                pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_TITLE, "Sort By Title");
                pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_FILE, "Sort By File Name");
                pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_DUR, "Sort By Duration");
                pPopup->AppendMenu(MF_SEPARATOR, 0, "");
                pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_SORT_REV, "Reverse List");
                pPopup->AppendMenu(MF_STRING | MF_ENABLED, ID_PL_RANDOM, "Randomize");
        }

        POINT pt;
        GetCursorPos(&pt);
        pPopup->TrackPopupMenu(TPM_RIGHTBUTTON, pt.x, pt.y, this);
        delete pPopup;

        *pResult = 0;
}

void Playlist::OnReverseSelection()
{
        u32 i;
        POSITION pos = m_FileList.GetFirstSelectedItemPosition();
        while (pos != NULL) {
                int nItem = m_FileList.GetNextSelectedItem(pos);
                PLEntry *ple = (PLEntry *) m_FileList.GetItemData(nItem);
                ple->m_bIsSelected = GF_TRUE;
        }

        for (i=0; i<gf_list_count(m_entries); i++) {
                PLEntry *ple = (PLEntry *) gf_list_get(m_entries, i);
                ple->m_bIsSelected = (Bool) !ple->m_bIsSelected;
        }
        RefreshList();
}

void Playlist::OnReverseList()
{
        u32 count = gf_list_count(m_entries);
        u32 hcount = count / 2;
        count--;
        for (u32 i=0; i<hcount; i++) {
                PLEntry *ple1 = (PLEntry *) gf_list_get(m_entries, i);
                PLEntry *ple2 = (PLEntry *) gf_list_get(m_entries, count-i);
                gf_list_rem(m_entries, i);
                gf_list_insert(m_entries, ple2, i);
                gf_list_rem(m_entries, count-i);
                gf_list_insert(m_entries, ple1, count-i);
        }
        RefreshList();
}

void Playlist::OnRandomize()
{
        GF_List *new_entries = gf_list_new();

        gf_rand_init(GF_FALSE);

        while (gf_list_count(m_entries)>1) {
                u32 pos = gf_rand() % (gf_list_count(m_entries)-1);
                PLEntry *ple = (PLEntry *)gf_list_get(m_entries, pos);
                gf_list_rem(m_entries, pos);
                gf_list_add(new_entries, ple);
        }
        PLEntry *ple = (PLEntry *)gf_list_get(m_entries, 0);
        gf_list_rem(m_entries, 0);
        gf_list_add(new_entries, ple);

        gf_list_del(m_entries);
        m_entries = new_entries;
        m_cur_entry = -1;
        RefreshList();
}

void Playlist::Sort(u32 type)
{
        u32 i, j, smallest;
        if (gf_list_count(m_entries)<=1) return;

        for (i=0; i<gf_list_count(m_entries)-1; i++) {
                smallest = i;
                for (j=i+1; j<gf_list_count(m_entries); j++) {
                        PLEntry *ple2 = (PLEntry *) gf_list_get(m_entries, smallest);
                        PLEntry *ple1 = (PLEntry *) gf_list_get(m_entries, j);
                        s32 test = 0;
                        switch (type) {
                        case 0:
                                test = stricmp(ple1->m_url, ple2->m_url);
                                break;
                        case 1:
                                test = stricmp(ple1->m_disp_name, ple2->m_disp_name);
                                break;
                        case 2:
                                test = ple1->m_duration - ple2->m_duration;
                                break;
                        }
                        if (test<0) smallest = j;
                }
                PLEntry *ple = (PLEntry *)gf_list_get(m_entries, smallest);
                gf_list_rem(m_entries, smallest);
                gf_list_insert(m_entries, ple, i);
        }
        m_cur_entry = -1;
        RefreshList();
}

void Playlist::OnSortFile() {
        Sort(0);
}
void Playlist::OnSortTitle() {
        Sort(1);
}
void Playlist::OnSortDuration() {
        Sort(2);
}


Bool Playlist::HasValidEntries()
{
        u32 nb_dead = 0;
        if (m_all_dead_entries==-1) {
                for (u32 i=0; i<gf_list_count(m_entries); i++) {
                        PLEntry *ple = (PLEntry *) gf_list_get(m_entries, i);
                        ple->m_bIsPlaying = GF_FALSE;
                        if (ple->m_bIsDead) nb_dead ++;
                }
                m_all_dead_entries = (nb_dead==gf_list_count(m_entries)) ? 1 : 0;
        }
        if (m_all_dead_entries==1) return GF_FALSE;
        return GF_TRUE;
}

void Playlist::RefreshCurrent()
{
        if (m_cur_entry==-1) return;
        PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry);
        if (ple && ple->m_bIsPlaying) {
                ple->m_bIsPlaying = GF_FALSE;
                UpdateEntry(m_cur_entry);
        }
}

void Playlist::Play()
{
        PLEntry *ple;

        if (!HasValidEntries()) return;

        RefreshCurrent();
        if (m_cur_entry==-1) m_cur_entry = 0;

        if (m_cur_entry >= (s32)gf_list_count(m_entries)) {
                if (!GetApp()->m_Loop) return;
                m_cur_entry = 0;
        }

        ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry);
        assert(ple);
        if (ple->m_bIsDead) {
                m_cur_entry++;
                Play();
        } else {
                char szPLE[20];
                ple->m_bIsPlaying = GF_TRUE;
                UpdateEntry(m_cur_entry);
                sprintf(szPLE, "%d", m_cur_entry);
                gf_cfg_set_key(GetApp()->m_user.config, "General", "PLEntry", szPLE);
                GetApp()->m_pMainWnd->PostMessage(WM_OPENURL);
        }
}

void Playlist::OnDblclkFilelist(NMHDR* pNMHDR, LRESULT* pResult)
{
        POSITION pos = m_FileList.GetFirstSelectedItemPosition();
        RefreshCurrent();
        m_cur_entry = m_FileList.GetNextSelectedItem(pos);
        Play();
        *pResult = 0;
}

void Playlist::OnPlPlay()
{
        POSITION pos = m_FileList.GetFirstSelectedItemPosition();

        RefreshCurrent();
        m_cur_entry = m_FileList.GetNextSelectedItem(pos);
        Play();
}

void Playlist::Truncate()
{
        while (m_cur_entry+1 < (s32)gf_list_count(m_entries)) {
                PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry+1);
                gf_list_rem(m_entries, m_cur_entry+1);
                delete ple;
        }
        RefreshList();
}


void Playlist::QueueURL(CString filename)
{
        char *ext = (char *) strrchr(filename, '.');
        if (ext && (!stricmp(ext, ".m3u") || !stricmp(ext, ".pls")) ) {
                OpenPlayList(filename);
        } else {
                PLEntry *ple = new PLEntry(filename);
                gf_list_add(m_entries, ple);
        }
        m_all_dead_entries=-1;
}

void Playlist::PlayNext()
{
        s32 count = (s32)gf_list_count(m_entries);

        RefreshCurrent();
        if (1+m_cur_entry < count) {
                m_cur_entry++;
                Play();
        } else if ((1+m_cur_entry == count) && GetApp()->m_Loop) {
                m_cur_entry=0;
                Play();
        }
}

void Playlist::PlayPrev()
{
        RefreshCurrent();
        if (m_cur_entry>0) {
                m_cur_entry--;
                Play();
        }
}

void Playlist::SetDead()
{
        PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry);
        if (ple) {
                ple->m_bIsDead = GF_TRUE;
                UpdateEntry(m_cur_entry);
                m_all_dead_entries=-1;
                if (ple->m_bIsPlaying) PlayNext();
        }
}
void Playlist::SetDuration(u32 duration)
{
        PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry);
        if (ple) {
                ple->m_duration = duration;
                UpdateEntry(m_cur_entry);
        }
}

CString Playlist::GetDisplayName()
{
        PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry);
        if (ple) return CString(ple->m_disp_name);
        return CString("");
}

CString Playlist::GetURL()
{
        PLEntry *ple = (PLEntry *) gf_list_get(m_entries, m_cur_entry);
        if (ple) return CString(ple->m_url);
        return CString("");
}


/* [<][>][^][v][top][bottom][index][help] */