xref: /MusicPlayer2/MusicPlayer2/AllMediaDlg.cpp (revision 965ce478a79b0d21e8a6e2ade0490efa175855dd)
1 // AllMediaDlg.cpp: 实现文件
2 //
3 
4 #include "stdafx.h"
5 #include "MusicPlayer2.h"
6 #include "Player.h"
7 #include "AllMediaDlg.h"
8 #include "MusicPlayerCmdHelper.h"
9 #include "PropertyDlg.h"
10 #include "SongDataManager.h"
11 
12 
13 // CAllMediaDlg 对话框
14 
IMPLEMENT_DYNAMIC(CAllMediaDlg,CMediaLibTabDlg)15 IMPLEMENT_DYNAMIC(CAllMediaDlg, CMediaLibTabDlg)
16 
17 CAllMediaDlg::CAllMediaDlg(CAllMediaDlg::DialogType type, CWnd* pParent /*=nullptr*/)
18     : CMediaLibTabDlg(IDD_ALL_MEDIA_DIALOG, pParent), m_type{ type }
19 {
20 
21 }
22 
~CAllMediaDlg()23 CAllMediaDlg::~CAllMediaDlg()
24 {
25 }
26 
RefreshData()27 void CAllMediaDlg::RefreshData()
28 {
29     InitListData();
30     ShowSongList();
31     m_initialized = true;
32 }
33 
RefreshSongList()34 void CAllMediaDlg::RefreshSongList()
35 {
36     for (int index : m_selected_items)
37     {
38         if (index >= 0 && index < static_cast<int>(m_list_data.size()))
39         {
40             SongInfo tmp = CSongDataManager::GetInstance().GetSongInfo3(m_list_songs[index]);
41             std::swap(m_list_songs[index], tmp);
42             SetRowData(m_list_data[index], m_list_songs[index]);
43         }
44     }
45     ShowSongList();
46 }
47 
OnTabEntered()48 void CAllMediaDlg::OnTabEntered()
49 {
50     if (m_type == DT_ALL_MEDIA)
51     {
52         SetButtonsEnable(true);
53         SetPlayButtonText(m_song_list_ctrl.GetCurSel() >= 0);
54     }
55     else
56     {
57         SetButtonsEnable(m_song_list_ctrl.GetCurSel() >= 0);
58     }
59     if (!m_initialized)
60     {
61         CWaitCursor wait_cursor;
62         InitListData();
63         ShowSongList();
64         m_initialized = true;
65     }
66 }
67 
OnTabExited()68 void CAllMediaDlg::OnTabExited()
69 {
70     //切换到其他标签时将按钮恢复为“播放选中”
71     if (m_type == DT_ALL_MEDIA)
72     {
73         CWnd* pParent = GetParentWindow();
74         if (pParent != nullptr)
75             pParent->SetDlgItemTextW(IDC_PLAY_SELECTED, theApp.m_str_table.LoadText(L"TXT_LIB_PLAY_SEL").c_str());
76     }
77 }
78 
InitListData()79 void CAllMediaDlg::InitListData()
80 {
81     m_list_data.clear();
82     m_list_songs.clear();
83 
84     //获取当前系统时间
85     SYSTEMTIME sys_time;
86     GetLocalTime(&sys_time);
87     __int64 cur_time = CTime(sys_time).GetTime();
88 
89     CSongDataManager::GetInstance().GetSongData([&](const CSongDataManager::SongDataMap& song_data_map)
90         {
91             for (const auto& item : song_data_map)
92             {
93                 if (m_type == DT_RECENT_MEDIA)      //如果显示最近播放曲目,则跳过没有播放过的曲目
94                 {
95                     if (item.second.last_played_time == 0)
96                         continue;
97 
98                     //计算曲目上一次播放的时间和当前的时间差
99                     __int64 time_span = cur_time - item.second.last_played_time;
100                     //如果时间差超过了列表显示的范围,则跳过它
101                     switch (theApp.m_media_lib_setting_data.recent_played_range)
102                     {
103                     case RPR_TODAY:
104                         if (time_span > 24 * 3600)
105                             continue;
106                         break;
107                     case RPR_THREE_DAYS:
108                         if (time_span > 3 * 24 * 3600)
109                             continue;
110                         break;
111                     case RPR_WEAK:
112                         if (time_span > 7 * 24 * 3600)
113                             continue;
114                         break;
115                     case RPR_MONTH:
116                         if (time_span > 30 * 24 * 3600)
117                             continue;
118                         break;
119                     case RPR_HALF_YEAR:
120                         if (time_span > 180 * 24 * 3600)
121                             continue;
122                         break;
123                     case RPR_YEAR:
124                         if (time_span > 360 * 24 * 3600)
125                             continue;
126                         break;
127                     default:
128                         break;
129                     }
130                 }
131                 m_list_songs.push_back(item.second);
132             }
133         });
134     std::sort(m_list_songs.begin(), m_list_songs.end(), [&](const SongInfo& a, const SongInfo& b)
135     {
136         // 显示所有曲目时默认按标题排序,显示最近曲目时默认按最近播放时间排序
137         if (m_type == DT_RECENT_MEDIA)
138             return a.last_played_time > b.last_played_time;
139         else
140             return CCommon::StringCompareInLocalLanguage(a.title, b.title) < 0;
141     });
142     for (const SongInfo& tmp : m_list_songs)
143     {
144         CListCtrlEx::RowData row_data;
145         SetRowData(row_data, tmp);
146         m_list_data.push_back(std::move(row_data));
147     }
148     UpdateListIndex();
149 }
150 
SetRowData(CListCtrlEx::RowData & row_data,const SongInfo & song)151 void CAllMediaDlg::SetRowData(CListCtrlEx::RowData& row_data, const SongInfo& song)
152 {
153     row_data[COL_TITLE] = song.GetTitle();
154     row_data[COL_ARTIST] = song.GetArtist();
155     row_data[COL_ALBUM] = song.GetAlbum();
156     std::wstring track_str;
157     if (song.track != 0)
158         track_str = std::to_wstring(song.track);
159     row_data[COL_TRACK] = track_str;
160     row_data[COL_GENRE] = song.GetGenre();
161     row_data[COL_BITRATE] = (song.bitrate == 0 ? L"-" : std::to_wstring(song.bitrate));
162     row_data[COL_YEAR] = song.GetYear();
163     row_data[COL_PATH] = song.file_path;
164     if (song.last_played_time != 0)
165     {
166         CTime played_time(song.last_played_time);
167         wchar_t buff[64];
168         swprintf_s(buff, L"%d/%.2d/%.2d %.2d:%.2d:%.2d", played_time.GetYear(), played_time.GetMonth(), played_time.GetDay(),
169             played_time.GetHour(), played_time.GetMinute(), played_time.GetSecond());
170         row_data[COL_LAST_PLAYED_TIME] = buff;
171     }
172 }
173 
UpdateListIndex()174 void CAllMediaDlg::UpdateListIndex()
175 {
176     int index{ 1 };
177     for (auto& item : m_list_data)
178     {
179         item[COL_INDEX] = std::to_wstring(index);
180         index++;
181     }
182 }
183 
ShowSongList()184 void CAllMediaDlg::ShowSongList()
185 {
186     CWaitCursor wait_cursor;
187     if (m_searched)
188         m_song_list_ctrl.SetListData(&m_list_data_searched);
189     else
190         m_song_list_ctrl.SetListData(&m_list_data);
191 }
192 
QuickSearch(const wstring & key_word)193 void CAllMediaDlg::QuickSearch(const wstring& key_word)
194 {
195     ASSERT(m_list_data.size() == m_list_songs.size());
196     m_list_data_searched.clear();
197     m_list_songs_searched.clear();
198 
199     for (int i{}; i < static_cast<int>(m_list_data.size()); ++i)
200     {
201         const auto& item{ m_list_data[i] };
202         const vector<int> search_col{ COL_TITLE, COL_ARTIST, COL_ALBUM, COL_GENRE, COL_PATH };
203         for (int col : search_col)
204         {
205             if (theApp.m_chinese_pingyin_res.IsStringMatchWithPingyin(key_word, item.at(col)))
206             {
207                 m_list_data_searched.push_back(item);
208                 m_list_songs_searched.push_back(m_list_songs[i]);
209                 break;
210             }
211         }
212     }
213 }
214 
SongListClicked(int index)215 void CAllMediaDlg::SongListClicked(int index)
216 {
217     m_selected_item = index;
218     m_song_list_ctrl.GetItemSelected(m_selected_items);
219     bool select_valid = !m_selected_items.empty();
220     if (m_type == DT_ALL_MEDIA)
221         SetPlayButtonText(select_valid);
222     else
223         SetButtonsEnable(select_valid);
224 }
225 
SetButtonsEnable(bool enable)226 void CAllMediaDlg::SetButtonsEnable(bool enable)
227 {
228     CWnd* pParent = GetParentWindow();
229     ::SendMessage(pParent->GetSafeHwnd(), WM_PLAY_SELECTED_BTN_ENABLE, WPARAM(enable), 0);
230 }
231 
SetPlayButtonText(bool selected_valid)232 void CAllMediaDlg::SetPlayButtonText(bool selected_valid)
233 {
234     CWnd* pParent = GetParentWindow();
235     if (pParent != nullptr)
236     {
237         //选中了曲目时按钮文本为“播放选中”,否则为“播放”
238         std::wstring text;
239         if (selected_valid)
240             text = theApp.m_str_table.LoadText(L"TXT_LIB_PLAY_SEL");
241         else
242             text = theApp.m_str_table.LoadText(L"UI_TIP_BTN_PLAY");
243         pParent->SetDlgItemTextW(IDC_PLAY_SELECTED, text.c_str());
244     }
245 }
246 
GetSongList() const247 const vector<SongInfo>& CAllMediaDlg::GetSongList() const
248 {
249     if (m_searched)
250         return m_list_songs_searched;
251     else
252         return m_list_songs;
253 }
254 
GetItemSelected() const255 int CAllMediaDlg::GetItemSelected() const
256 {
257     return m_selected_item;
258 }
259 
GetItemsSelected() const260 const vector<int>& CAllMediaDlg::GetItemsSelected() const
261 {
262     return m_selected_items;
263 }
264 
AfterDeleteFromDisk(const std::vector<SongInfo> & songs)265 void CAllMediaDlg::AfterDeleteFromDisk(const std::vector<SongInfo>& songs)
266 {
267     //删除成功,则刷新列表
268     auto isRemoved_song = [&](const SongInfo& data)
269     {
270         for (const auto& item : songs)
271         {
272             if (item.IsSameSong(data))
273                 return true;
274         }
275         return false;
276     };
277     auto iter_removed_song = std::remove_if(m_list_songs.begin(), m_list_songs.end(), isRemoved_song);
278     m_list_songs.erase(iter_removed_song, m_list_songs.end());
279     auto iter_removed_song1 = std::remove_if(m_list_songs_searched.begin(), m_list_songs_searched.end(), isRemoved_song);
280     m_list_songs_searched.erase(iter_removed_song1, m_list_songs_searched.end());
281 
282     auto isRemoved = [&](const CListCtrlEx::RowData& data)
283     {
284         for (const auto& item : songs)
285         {
286             if (item.file_path == data.at(COL_PATH) && item.track == std::stoi(data.at(COL_TRACK)))     // 这里若遇到特殊文件可能有潜在问题(音频内嵌cue)
287                 return true;
288         }
289         return false;
290     };
291     auto iter_removed = std::remove_if(m_list_data.begin(), m_list_data.end(), isRemoved);
292     m_list_data.erase(iter_removed, m_list_data.end());
293     auto iter_removed1 = std::remove_if(m_list_data_searched.begin(), m_list_data_searched.end(), isRemoved);
294     m_list_data_searched.erase(iter_removed1, m_list_data_searched.end());
295     ShowSongList();
296 }
297 
GetSelectedString() const298 wstring CAllMediaDlg::GetSelectedString() const
299 {
300     return m_selected_string;
301 }
302 
DoDataExchange(CDataExchange * pDX)303 void CAllMediaDlg::DoDataExchange(CDataExchange* pDX)
304 {
305     CMediaLibTabDlg::DoDataExchange(pDX);
306     DDX_Control(pDX, IDC_SONG_LIST, m_song_list_ctrl);
307     DDX_Control(pDX, IDC_SEARCH_EDIT, m_search_edit);
308 }
309 
310 
OnOK()311 void CAllMediaDlg::OnOK()
312 {
313     //这里重写了基类CMediaLibTabDlg的OnOK函数
314     //在“所有曲目”和“最近播放”标签中双击一项,会在“默认”播放列表中打开选中的曲目
315     // 多选时OnOK会将选中项添加到临时播放列表内
316 
317     vector<SongInfo> songs;
318     GetSongsSelected(songs);
319     if (!songs.empty() || m_type == DT_ALL_MEDIA)
320     {
321         bool ok{};
322         // 所有曲目单选时使用媒体库模式播放
323         if (m_type == DT_ALL_MEDIA && songs.size() == 1)
324         {
325             ListItem list_item{ LT_MEDIA_LIB, L"", CMediaClassifier::CT_NONE};
326             list_item.SetPlayTrack(songs.front());
327             ok = CPlayer::GetInstance().SetList(list_item, true, true);
328         }
329         else
330         {
331             if (songs.size() == 1)
332                 ok = CPlayer::GetInstance().OpenSongsInDefaultPlaylist(songs);
333             else
334                 ok = CPlayer::GetInstance().OpenSongsInTempPlaylist(songs);
335         }
336         if (!ok)
337         {
338             const wstring& info = theApp.m_str_table.LoadText(L"MSG_WAIT_AND_RETRY");
339             MessageBox(info.c_str(), NULL, MB_ICONINFORMATION | MB_OK);
340         }
341         else
342         {
343             CTabDlg::OnOK();
344             CWnd* pParent = GetParentWindow();
345             if (pParent != nullptr)
346                 ::PostMessage(pParent->GetSafeHwnd(), WM_COMMAND, IDOK, 0);
347         }
348     }
349 }
350 
BEGIN_MESSAGE_MAP(CAllMediaDlg,CMediaLibTabDlg)351 BEGIN_MESSAGE_MAP(CAllMediaDlg, CMediaLibTabDlg)
352     ON_NOTIFY(HDN_ITEMCLICK, 0, &CAllMediaDlg::OnHdnItemclickSongList)
353     ON_EN_CHANGE(IDC_SEARCH_EDIT, &CAllMediaDlg::OnEnChangeSearchEdit)
354     ON_NOTIFY(NM_CLICK, IDC_SONG_LIST, &CAllMediaDlg::OnNMClickSongList)
355     ON_NOTIFY(NM_RCLICK, IDC_SONG_LIST, &CAllMediaDlg::OnNMRClickSongList)
356     ON_NOTIFY(NM_DBLCLK, IDC_SONG_LIST, &CAllMediaDlg::OnNMDblclkSongList)
357     ON_MESSAGE(WM_SEARCH_EDIT_BTN_CLICKED, &CAllMediaDlg::OnSearchEditBtnClicked)
358     ON_WM_INITMENU()
359 END_MESSAGE_MAP()
360 
361 
362 // CAllMediaDlg 消息处理程序
363 
364 
365 BOOL CAllMediaDlg::OnInitDialog()
366 {
367     CMediaLibTabDlg::OnInitDialog();
368 
369     // TODO:  在此添加额外的初始化
370 
371     //初始化歌曲列表
372     m_song_list_ctrl.SetExtendedStyle(m_song_list_ctrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_LABELTIP);
373     m_song_list_ctrl.InsertColumn(0, theApp.m_str_table.LoadText(L"TXT_SERIAL_NUMBER").c_str(), LVCFMT_LEFT, theApp.DPI(40));
374     m_song_list_ctrl.InsertColumn(1, theApp.m_str_table.LoadText(L"TXT_TITLE").c_str(), LVCFMT_LEFT, theApp.DPI(150));
375     m_song_list_ctrl.InsertColumn(2, theApp.m_str_table.LoadText(L"TXT_ARTIST").c_str(), LVCFMT_LEFT, theApp.DPI(100));
376     m_song_list_ctrl.InsertColumn(3, theApp.m_str_table.LoadText(L"TXT_ALBUM").c_str(), LVCFMT_LEFT, theApp.DPI(150));
377     m_song_list_ctrl.InsertColumn(4, theApp.m_str_table.LoadText(L"TXT_TRACK_NUM").c_str(), LVCFMT_LEFT, theApp.DPI(60));
378     m_song_list_ctrl.InsertColumn(5, theApp.m_str_table.LoadText(L"TXT_GENRE").c_str(), LVCFMT_LEFT, theApp.DPI(100));
379     m_song_list_ctrl.InsertColumn(6, theApp.m_str_table.LoadText(L"TXT_BITRATE").c_str(), LVCFMT_LEFT, theApp.DPI(60));
380     m_song_list_ctrl.InsertColumn(7, theApp.m_str_table.LoadText(L"TXT_YEAR").c_str(), LVCFMT_LEFT, theApp.DPI(60));
381     m_song_list_ctrl.InsertColumn(8, theApp.m_str_table.LoadText(L"TXT_FILE_PATH").c_str(), LVCFMT_LEFT, theApp.DPI(600));
382     m_song_list_ctrl.InsertColumn(9, theApp.m_str_table.LoadText(L"TXT_LAST_PLAYED_TIME").c_str(), LVCFMT_LEFT, theApp.DPI(140));
383     m_song_list_ctrl.SetCtrlAEnable(true);
384 
385     m_search_edit.SetCueBanner(theApp.m_str_table.LoadText(L"TXT_SEARCH_PROMPT").c_str(), TRUE);
386 
387     return TRUE;  // return TRUE unless you set the focus to a control
388                   // 异常: OCX 属性页应返回 FALSE
389 }
390 
391 
OnHdnItemclickSongList(NMHDR * pNMHDR,LRESULT * pResult)392 void CAllMediaDlg::OnHdnItemclickSongList(NMHDR *pNMHDR, LRESULT *pResult)
393 {
394     LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
395     // TODO: 在此添加控件通知处理程序代码
396     if (phdr->hdr.hwndFrom == m_song_list_ctrl.GetHeaderCtrl()->GetSafeHwnd())
397     {
398         static bool ascending = false;
399         ascending = !ascending;
400 
401         static int last_item = -1;
402         if (last_item != phdr->iItem)
403         {
404             last_item = phdr->iItem;
405             ascending = true;
406         }
407 
408         //对列表排序
409         auto& list_songs{ m_searched ? m_list_songs_searched : m_list_songs };
410         auto& list_data{ m_searched ? m_list_data_searched : m_list_data };
411 
412         if (phdr->iItem > COL_INDEX && phdr->iItem < COL_MAX)
413         {
414             SortMode sort_mode = SM_U_TITLE;
415             switch (phdr->iItem)
416             {
417             case COL_TITLE: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
418             case COL_ARTIST: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
419             case COL_ALBUM: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
420             case COL_TRACK: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
421             case COL_GENRE: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
422             case COL_BITRATE: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
423             case COL_YEAR: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
424             case COL_PATH: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
425             case COL_LAST_PLAYED_TIME: sort_mode = ascending ? SM_U_TITLE : SM_D_TITLE; break;
426             default: break;
427             }
428             auto so = CCommon::sort_permutation(list_songs, SongInfo::GetSortFunc(sort_mode));
429             list_data = CCommon::apply_permutation(list_data, so);
430             list_songs = CCommon::apply_permutation(list_songs, so);
431             if (!m_searched)
432                 UpdateListIndex();
433             ShowSongList();
434         }
435     }
436 
437     *pResult = 0;
438 }
439 
440 
OnEnChangeSearchEdit()441 void CAllMediaDlg::OnEnChangeSearchEdit()
442 {
443     // TODO:  如果该控件是 RICHEDIT 控件,它将不
444     // 发送此通知,除非重写 CMediaLibTabDlg::OnInitDialog()
445     // 函数并调用 CRichEditCtrl().SetEventMask(),
446     // 同时将 ENM_CHANGE 标志“或”运算到掩码中。
447 
448     // TODO:  在此添加控件通知处理程序代码
449     CString str;
450     m_search_edit.GetWindowText(str);
451     QuickSearch(wstring(str));
452     m_searched = !str.IsEmpty();
453     ShowSongList();
454 }
455 
456 
OnNMClickSongList(NMHDR * pNMHDR,LRESULT * pResult)457 void CAllMediaDlg::OnNMClickSongList(NMHDR *pNMHDR, LRESULT *pResult)
458 {
459     LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
460     // TODO: 在此添加控件通知处理程序代码
461     SongListClicked(pNMItemActivate->iItem);
462     *pResult = 0;
463 }
464 
465 
OnNMRClickSongList(NMHDR * pNMHDR,LRESULT * pResult)466 void CAllMediaDlg::OnNMRClickSongList(NMHDR *pNMHDR, LRESULT *pResult)
467 {
468     LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
469     // TODO: 在此添加控件通知处理程序代码
470     SongListClicked(pNMItemActivate->iItem);
471     m_selected_string = m_song_list_ctrl.GetItemText(pNMItemActivate->iItem, pNMItemActivate->iSubItem);
472 
473     if (pNMItemActivate->iItem >= 0)
474     {
475         //弹出右键菜单
476         CMenu* pMenu = theApp.m_menu_mgr.GetMenu(MenuMgr::LibRightMenu);
477         ASSERT(pMenu != nullptr);
478         if (pMenu != nullptr)
479         {
480             m_song_list_ctrl.ShowPopupMenu(pMenu, pNMItemActivate->iItem, this);
481         }
482     }
483     *pResult = 0;
484 }
485 
486 
OnNMDblclkSongList(NMHDR * pNMHDR,LRESULT * pResult)487 void CAllMediaDlg::OnNMDblclkSongList(NMHDR *pNMHDR, LRESULT *pResult)
488 {
489     LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
490     // TODO: 在此添加控件通知处理程序代码
491     OnOK();
492     *pResult = 0;
493 }
494 
495 
OnSearchEditBtnClicked(WPARAM wParam,LPARAM lParam)496 afx_msg LRESULT CAllMediaDlg::OnSearchEditBtnClicked(WPARAM wParam, LPARAM lParam)
497 {
498     //点击搜索框中的叉按钮时清除搜索结果
499     if (m_searched)
500     {
501         //清除搜索结果
502         m_searched = false;
503         m_search_edit.SetWindowText(_T(""));
504         ShowSongList();
505     }
506     return 0;
507 }
508 
509 
510 
OnInitMenu(CMenu * pMenu)511 void CAllMediaDlg::OnInitMenu(CMenu* pMenu)
512 {
513     CMediaLibTabDlg::OnInitMenu(pMenu);
514 
515     //设置“添加到播放列表”子菜单状态
516     //未选中状态不会弹出右键菜单,因此“添加到播放列表”子菜单全部设置为可用状态
517     for (UINT id = ID_ADD_TO_DEFAULT_PLAYLIST; id < ID_ADD_TO_MY_FAVOURITE + ADD_TO_PLAYLIST_MAX_SIZE + 1; id++)
518     {
519         pMenu->EnableMenuItem(id, MF_BYCOMMAND | MF_ENABLED);
520     }
521     pMenu->EnableMenuItem(ID_ADD_TO_NEW_PLAYLIST, MF_BYCOMMAND | MF_ENABLED);
522     pMenu->EnableMenuItem(ID_ADD_TO_OTHER_PLAYLIST, MF_BYCOMMAND | MF_ENABLED);
523 }
524