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