xref: /MusicPlayer2/MusicPlayer2/FolderExploreDlg.cpp (revision b02ea78914f3f3cea834525e3ae2052cd91d1e6d)
1 // FolderExploreDlg.cpp: 实现文件
2 //
3 
4 #include "stdafx.h"
5 #include "MusicPlayer2.h"
6 #include "Player.h"
7 #include "FolderExploreDlg.h"
8 #include "AudioCommon.h"
9 #include "MusicPlayerCmdHelper.h"
10 #include "PropertyDlg.h"
11 #include "Playlist.h"
12 #include "AddToPlaylistDlg.h"
13 #include "SongDataManager.h"
14 
15 
16 // CFolderExploreDlg 对话框
17 
IMPLEMENT_DYNAMIC(CFolderExploreDlg,CMediaLibTabDlg)18 IMPLEMENT_DYNAMIC(CFolderExploreDlg, CMediaLibTabDlg)
19 
20 CFolderExploreDlg::CFolderExploreDlg(CWnd* pParent /*=nullptr*/)
21     : CMediaLibTabDlg(IDD_FOLDER_EXPLORE_DIALOG, pParent)
22 {
23 
24 }
25 
~CFolderExploreDlg()26 CFolderExploreDlg::~CFolderExploreDlg()
27 {
28 }
29 
RefreshData()30 void CFolderExploreDlg::RefreshData()
31 {
32     ShowFolderTree();
33 }
34 
GetSongsSelected(std::vector<SongInfo> & song_list) const35 void CFolderExploreDlg::GetSongsSelected(std::vector<SongInfo>& song_list) const
36 {
37     if (m_left_selected)
38     {
39         CAudioCommon::GetAudioFiles(m_folder_path_selected, song_list, 20000, true);
40     }
41     else
42     {
43         CMediaLibTabDlg::GetSongsSelected(song_list);
44     }
45 }
46 
RefreshSongList()47 void CFolderExploreDlg::RefreshSongList()
48 {
49     ShowSongList();
50 }
51 
ShowFolderTree()52 void CFolderExploreDlg::ShowFolderTree()
53 {
54     m_initialized = true;
55     CString search_key_word;
56     m_search_edit.GetWindowText(search_key_word);
57     m_searched = !search_key_word.IsEmpty();
58     if (m_searched)     //要进入搜索状态前,先保存当前树的展开收缩状态
59         m_folder_explore_tree.SaveExpandState();
60 
61     m_folder_explore_tree.DeleteAllItems();
62     //wstring default_folder = CCommon::GetSpecialDir(CSIDL_MYMUSIC);
63     for(const auto& default_folder : theApp.m_media_lib_setting_data.media_folders)
64     {
65         m_folder_explore_tree.InsertPath(default_folder.c_str(), NULL, [&](const CString& folder_path)
66         {
67             if (!CAudioCommon::IsPathContainsAudioFile(wstring(folder_path), true))       //排除不包含音频文件的目录
68                 return false;
69             if (m_searched)
70                 return CCommon::IsFolderMatchKeyWord(wstring(folder_path), wstring(search_key_word));
71             return true;
72         });
73     }
74 
75     if (m_searched)
76         m_folder_explore_tree.ExpandAll();
77     else
78         m_folder_explore_tree.RestoreExpandState();     //不是搜索状态,树刷新后恢复展开收缩状态
79 }
80 
ShowSongList()81 void CFolderExploreDlg::ShowSongList()
82 {
83     CWaitCursor wait_cursor;
84     SetDlgItemText(IDC_PATH_STATIC, m_folder_path_selected.c_str());
85 
86     //获取歌曲列表
87     m_right_items.clear();
88 
89     // 此处在主线程更新cue条目到媒体库并加载歌曲信息,如果有大量cue且没有提前“更新媒体库”第一次进行耗时可能很长
90     // 播放左侧目录的OpenFolder包含子文件夹所以这里也包含子文件夹
91     CAudioCommon::GetAudioFiles(m_folder_path_selected, m_right_items, MAX_SONG_NUM, true);
92 
93     bool exit_flag{};
94     int update_cnt{};
95     // 这里仍然使用GetCueTracks而不是GetAudioInfo是因为后者涉及忽略短文件设置,实际执行的OpenFolder无视设置加入短文件
96     // 我希望此处预览(至少数量上)与OpenFolder结果一致,但不希望只为看一下(预览)就把无关(短)文件加入媒体库
97     CAudioCommon::GetCueTracks(m_right_items, update_cnt, exit_flag, MR_MIN_REQUIRED);
98     // 加载歌曲信息
99     CSongDataManager::GetInstance().LoadSongsInfo(m_right_items);
100 
101     //显示到列表控件中
102     m_list_data.clear();
103     for (const auto& item : m_right_items)
104     {
105         CListCtrlEx::RowData row_data;
106         row_data[COL_FILE_NAME] = item.GetFileName();
107         row_data[COL_TITLE] = item.GetTitle();
108         row_data[COL_ARTIST] = item.GetArtist();
109         row_data[COL_ALBUM] = item.GetAlbum();
110         row_data[COL_PATH] = item.file_path;
111         m_list_data.push_back(std::move(row_data));
112     }
113     m_song_list_ctrl.SetListData(&m_list_data);
114 }
115 
FolderTreeClicked(HTREEITEM hItem)116 void CFolderExploreDlg::FolderTreeClicked(HTREEITEM hItem)
117 {
118     m_left_selected = true;
119     wstring folder_path_selected{ m_folder_explore_tree.GetItemPath(hItem) };
120     if (folder_path_selected != m_folder_path_selected)
121     {
122         m_folder_path_selected = folder_path_selected;
123         ShowSongList();
124     }
125     m_right_selected_item = -1;         // 点击左侧列表时清空右侧列表选中项
126     m_right_selected_items.clear();
127     m_song_list_ctrl.SelectNone();
128     SetButtonsEnable(CCommon::FolderExist(folder_path_selected));
129 }
130 
SongListClicked(int index)131 void CFolderExploreDlg::SongListClicked(int index)
132 {
133     m_left_selected = false;
134     m_right_selected_item = index;
135     m_song_list_ctrl.GetItemSelected(m_right_selected_items);
136     SetButtonsEnable(!m_right_selected_items.empty());
137 }
138 
SetButtonsEnable(bool enable)139 void CFolderExploreDlg::SetButtonsEnable(bool enable)
140 {
141     CWnd* pParent = GetParentWindow();
142     ::SendMessage(pParent->GetSafeHwnd(), WM_PLAY_SELECTED_BTN_ENABLE, WPARAM(enable), 0);
143 }
144 
GetNewPlaylistName() const145 wstring CFolderExploreDlg::GetNewPlaylistName() const
146 {
147     std::wstring default_name;
148     //如果选中了左侧列表,则添加到新建播放列表时名称自动填上选中项的名称
149     default_name = m_folder_explore_tree.GetItemText(m_tree_item_selected);
150     CCommon::FileNameNormalize(default_name);
151     return default_name;
152 }
153 
OnTabEntered()154 void CFolderExploreDlg::OnTabEntered()
155 {
156     if (!m_initialized)
157     {
158         CWaitCursor wait_cursor;
159         //注意,在这里向左侧树填充数据可能会比较缓慢,因此放到这里处理,而不在OnInitDialog()中处理,
160         //即只有当标签切换到“文件夹浏览”时才填充数据,以加快“媒体库”对话框的打开速度
161         ShowFolderTree();
162     }
163     bool play_enable;
164     if (m_left_selected)
165         play_enable = CCommon::FolderExist(m_folder_path_selected);
166     else
167         play_enable = (!m_right_selected_items.empty());
168     SetButtonsEnable(play_enable);
169 }
170 
GetSongList() const171 const vector<SongInfo>& CFolderExploreDlg::GetSongList() const
172 {
173     return m_right_items;
174 }
175 
GetItemSelected() const176 int CFolderExploreDlg::GetItemSelected() const
177 {
178     return m_right_selected_item;
179 }
180 
GetItemsSelected() const181 const vector<int>& CFolderExploreDlg::GetItemsSelected() const
182 {
183     return m_right_selected_items;
184 }
185 
AfterDeleteFromDisk(const std::vector<SongInfo> & files)186 void CFolderExploreDlg::AfterDeleteFromDisk(const std::vector<SongInfo>& files)
187 {
188     //删除成功,则刷新列表
189     ShowSongList();
190 }
191 
GetSelectedString() const192 wstring CFolderExploreDlg::GetSelectedString() const
193 {
194     return wstring(m_selected_string);
195 }
196 
DoDataExchange(CDataExchange * pDX)197 void CFolderExploreDlg::DoDataExchange(CDataExchange* pDX)
198 {
199     CMediaLibTabDlg::DoDataExchange(pDX);
200     DDX_Control(pDX, IDC_MFCEDITBROWSE1, m_search_edit);
201     DDX_Control(pDX, IDC_FOLDER_EXPLORE_TREE, m_folder_explore_tree);
202     DDX_Control(pDX, IDC_SONG_LIST, m_song_list_ctrl);
203     DDX_Control(pDX, IDC_HSPLITER_STATIC, m_splitter_ctrl);
204 }
205 
206 
BEGIN_MESSAGE_MAP(CFolderExploreDlg,CMediaLibTabDlg)207 BEGIN_MESSAGE_MAP(CFolderExploreDlg, CMediaLibTabDlg)
208     ON_NOTIFY(NM_RCLICK, IDC_FOLDER_EXPLORE_TREE, &CFolderExploreDlg::OnNMRClickFolderExploreTree)
209     ON_NOTIFY(NM_CLICK, IDC_FOLDER_EXPLORE_TREE, &CFolderExploreDlg::OnNMClickFolderExploreTree)
210     ON_NOTIFY(NM_CLICK, IDC_SONG_LIST, &CFolderExploreDlg::OnNMClickSongList)
211     ON_NOTIFY(NM_RCLICK, IDC_SONG_LIST, &CFolderExploreDlg::OnNMRClickSongList)
212     ON_NOTIFY(NM_DBLCLK, IDC_FOLDER_EXPLORE_TREE, &CFolderExploreDlg::OnNMDblclkFolderExploreTree)
213     ON_NOTIFY(NM_DBLCLK, IDC_SONG_LIST, &CFolderExploreDlg::OnNMDblclkSongList)
214     ON_EN_CHANGE(IDC_MFCEDITBROWSE1, &CFolderExploreDlg::OnEnChangeMfceditbrowse1)
215     ON_MESSAGE(WM_SEARCH_EDIT_BTN_CLICKED, &CFolderExploreDlg::OnSearchEditBtnClicked)
216     ON_COMMAND(ID_BROWSE_PATH, &CFolderExploreDlg::OnBrowsePath)
217 END_MESSAGE_MAP()
218 
219 
220 // CFolderExploreDlg 消息处理程序
221 
222 
223 BOOL CFolderExploreDlg::OnInitDialog()
224 {
225     CMediaLibTabDlg::OnInitDialog();
226 
227     // TODO:  在此添加额外的初始化
228 
229     //为树控件设置图标
230     CSize icon_size = IconMgr::GetIconSize(IconMgr::IconSize::IS_DPI_16);
231     CImageList ImageList;
232     ImageList.Create(icon_size.cx, icon_size.cy, ILC_COLOR32 | ILC_MASK, 2, 2);
233     ImageList.Add(theApp.m_icon_mgr.GetHICON(IconMgr::IconType::IT_Folder, IconMgr::IconStyle::IS_Color, IconMgr::IconSize::IS_DPI_16));
234     m_folder_explore_tree.SetImageList(&ImageList, TVSIL_NORMAL);
235     ImageList.Detach();
236 
237     //设置行高
238     m_folder_explore_tree.SetItemHeight(theApp.DPI(22));
239 
240     if(!theApp.m_media_lib_setting_data.show_tree_tool_tips)
241         m_folder_explore_tree.ModifyStyle(0, TVS_NOTOOLTIPS);
242 
243     //填充数据
244     //注意,在这里向左侧树填充数据可能会比较缓慢,因此放到OnTabEntered()函数中处理,
245     //即只有当标签切换到“文件夹浏览”时才填充数据,以加载“媒体库”对话框的打开速度
246     //ShowFolderTree();
247 
248     //初始化右侧列表
249     m_song_list_ctrl.SetExtendedStyle(m_song_list_ctrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_LABELTIP);
250     //CRect rc_song_list;
251     //m_song_list_ctrl.GetWindowRect(rc_song_list);
252     m_song_list_ctrl.InsertColumn(0, theApp.m_str_table.LoadText(L"TXT_FILE_NAME").c_str(), LVCFMT_LEFT, theApp.DPI(200));
253     m_song_list_ctrl.InsertColumn(1, theApp.m_str_table.LoadText(L"TXT_TITLE").c_str(), LVCFMT_LEFT, theApp.DPI(180));
254     m_song_list_ctrl.InsertColumn(2, theApp.m_str_table.LoadText(L"TXT_ARTIST").c_str(), LVCFMT_LEFT, theApp.DPI(100));
255     m_song_list_ctrl.InsertColumn(3, theApp.m_str_table.LoadText(L"TXT_ALBUM").c_str(), LVCFMT_LEFT, theApp.DPI(100));
256     m_song_list_ctrl.InsertColumn(4, theApp.m_str_table.LoadText(L"TXT_FILE_PATH").c_str(), LVCFMT_LEFT, theApp.DPI(600));
257     m_song_list_ctrl.SetCtrlAEnable(true);
258 
259     m_search_edit.SetCueBanner(theApp.m_str_table.LoadText(L"TXT_SEARCH_PROMPT_FORDER").c_str(), TRUE);
260 
261     //初始化分隔条
262     m_splitter_ctrl.AttachCtrlAsLeftPane(IDC_FOLDER_EXPLORE_TREE);
263     m_splitter_ctrl.AttachCtrlAsLeftPane(IDC_MFCEDITBROWSE1);
264     m_splitter_ctrl.AttachCtrlAsRightPane(IDC_PATH_STATIC);
265     m_splitter_ctrl.AttachCtrlAsRightPane(IDC_SONG_LIST);
266 
267     return TRUE;  // return TRUE unless you set the focus to a control
268                   // 异常: OCX 属性页应返回 FALSE
269 }
270 
271 
OnNMRClickFolderExploreTree(NMHDR * pNMHDR,LRESULT * pResult)272 void CFolderExploreDlg::OnNMRClickFolderExploreTree(NMHDR *pNMHDR, LRESULT *pResult)
273 {
274     // TODO: 在此添加控件通知处理程序代码
275     if (pNMHDR->hwndFrom == m_folder_explore_tree.GetSafeHwnd())
276     {
277         CPoint point(GetMessagePos());
278         unsigned int nFlags = 0;
279         m_folder_explore_tree.ScreenToClient(&point);
280         HTREEITEM hItem = m_folder_explore_tree.HitTest(point, &nFlags);
281         m_tree_item_selected = hItem;
282         m_selected_string = m_folder_explore_tree.GetItemText(hItem);
283 
284         m_folder_explore_tree.SetFocus();
285         m_folder_explore_tree.SelectItem(hItem);
286         if ((nFlags & TVHT_ONITEM || nFlags & TVHT_ONITEMRIGHT || nFlags & TVHT_ONITEMINDENT) && (hItem != NULL))
287         {
288             FolderTreeClicked(hItem);
289 
290             GetCursorPos(&point);
291             if(hItem != NULL)
292             {
293                 CRect item_rect;
294                 m_folder_explore_tree.GetItemRect(hItem, item_rect, FALSE);
295                 CRect window_rect;
296                 m_folder_explore_tree.GetWindowRect(window_rect);       //获取列表控件的矩形区域(以屏幕左上角为原点)
297                 point.y = window_rect.top + item_rect.bottom;   //设置鼠标要弹出的y坐标为选中项目的下边框位置,防止右键菜单挡住选中的项目
298             }
299             CMenu* pMenu = theApp.m_menu_mgr.GetMenu(MenuMgr::LibFolderExploreMenu);
300             pMenu->TrackPopupMenu(TPM_LEFTBUTTON | TPM_LEFTALIGN, point.x, point.y, this);
301         }
302     }
303 
304     *pResult = 0;
305 }
306 
307 
OnNMClickFolderExploreTree(NMHDR * pNMHDR,LRESULT * pResult)308 void CFolderExploreDlg::OnNMClickFolderExploreTree(NMHDR *pNMHDR, LRESULT *pResult)
309 {
310     // TODO: 在此添加控件通知处理程序代码
311     if (pNMHDR->hwndFrom == m_folder_explore_tree.GetSafeHwnd())
312     {
313         CPoint point(GetMessagePos());
314         unsigned int nFlags = 0;
315         m_folder_explore_tree.ScreenToClient(&point);
316         HTREEITEM hItem = m_folder_explore_tree.HitTest(point, &nFlags);
317         m_tree_item_selected = hItem;
318         if ((nFlags & TVHT_ONITEM) && (hItem != NULL))
319         {
320             FolderTreeClicked(hItem);
321         }
322 
323         //点击加减号时
324         if ((nFlags & TVHT_ONITEMBUTTON) && (hItem != NULL))
325         {
326             bool expand = (m_folder_explore_tree.GetItemState(hItem, TVIS_EXPANDED)&TVIS_EXPANDED) == TVIS_EXPANDED;
327             //保存当前点击节点的展开状态,由于这时item的状态还没有改变,所以要对expand取反
328             m_folder_explore_tree.SaveItemExpandState(hItem, !expand);
329         }
330 
331         //m_folder_explore_tree.SetFocus();
332         //m_folder_explore_tree.SelectItem(hItem);
333     }
334     *pResult = 0;
335 }
336 
337 
OnNMClickSongList(NMHDR * pNMHDR,LRESULT * pResult)338 void CFolderExploreDlg::OnNMClickSongList(NMHDR *pNMHDR, LRESULT *pResult)
339 {
340     LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
341     // TODO: 在此添加控件通知处理程序代码
342     SongListClicked(pNMItemActivate->iItem);
343     *pResult = 0;
344 }
345 
346 
OnNMRClickSongList(NMHDR * pNMHDR,LRESULT * pResult)347 void CFolderExploreDlg::OnNMRClickSongList(NMHDR *pNMHDR, LRESULT *pResult)
348 {
349     LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
350     // TODO: 在此添加控件通知处理程序代码
351     SongListClicked(pNMItemActivate->iItem);
352     m_selected_string = m_song_list_ctrl.GetItemText(pNMItemActivate->iItem, pNMItemActivate->iSubItem);
353 
354     if(!m_right_selected_items.empty())
355     {
356         //弹出右键菜单
357         CMenu* pMenu = theApp.m_menu_mgr.GetMenu(MenuMgr::LibRightMenu);
358         ASSERT(pMenu != nullptr);
359         if (pMenu != nullptr)
360         {
361             m_song_list_ctrl.ShowPopupMenu(pMenu, pNMItemActivate->iItem, this);
362         }
363     }
364     *pResult = 0;
365 }
366 
367 
OnNMDblclkFolderExploreTree(NMHDR * pNMHDR,LRESULT * pResult)368 void CFolderExploreDlg::OnNMDblclkFolderExploreTree(NMHDR *pNMHDR, LRESULT *pResult)
369 {
370     // TODO: 在此添加控件通知处理程序代码
371     CPoint point(GetMessagePos());
372     unsigned int nFlags = 0;
373     m_folder_explore_tree.ScreenToClient(&point);
374     HTREEITEM hItem = m_folder_explore_tree.HitTest(point, &nFlags);
375     if ((nFlags & TVHT_ONITEM) && (hItem != NULL))
376     {
377         OnOK();
378     }
379 
380     *pResult = 0;
381 }
382 
383 
OnNMDblclkSongList(NMHDR * pNMHDR,LRESULT * pResult)384 void CFolderExploreDlg::OnNMDblclkSongList(NMHDR *pNMHDR, LRESULT *pResult)
385 {
386     LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
387     // TODO: 在此添加控件通知处理程序代码
388     OnOK();
389     *pResult = 0;
390 }
391 
392 
OnEnChangeMfceditbrowse1()393 void CFolderExploreDlg::OnEnChangeMfceditbrowse1()
394 {
395     // TODO:  如果该控件是 RICHEDIT 控件,它将不
396     // 发送此通知,除非重写 CMediaLibTabDlg::OnInitDialog()
397     // 函数并调用 CRichEditCtrl().SetEventMask(),
398     // 同时将 ENM_CHANGE 标志“或”运算到掩码中。
399 
400     // TODO:  在此添加控件通知处理程序代码
401     ShowFolderTree();
402 }
403 
404 
OnSearchEditBtnClicked(WPARAM wParam,LPARAM lParam)405 afx_msg LRESULT CFolderExploreDlg::OnSearchEditBtnClicked(WPARAM wParam, LPARAM lParam)
406 {
407     if(m_searched)
408     {
409         m_search_edit.SetWindowText(_T(""));
410     }
411     return 0;
412 }
413 
414 
OnOK()415 void CFolderExploreDlg::OnOK()
416 {
417     // TODO: 在此添加专用代码和/或调用基类
418     if (m_left_selected)        //选中左侧树时,播放选中文件夹
419     {
420         wstring folder_path{ m_folder_explore_tree.GetItemPath(m_tree_item_selected) };
421         CMusicPlayerCmdHelper helper(this);
422         if (helper.OnOpenFolder(folder_path, true, true))
423         {
424             CTabDlg::OnOK();
425             CWnd* pParent = GetParentWindow();
426             if (pParent != nullptr)
427                 ::PostMessage(pParent->GetSafeHwnd(), WM_COMMAND, IDOK, 0);
428         }
429     }
430     else
431     {
432         CMediaLibTabDlg::OnOK();
433     }
434 }
435 
436 
OnBrowsePath()437 void CFolderExploreDlg::OnBrowsePath()
438 {
439     // TODO: 在此添加命令处理程序代码
440     if (m_left_selected)        //选中左侧树时,播放选中文件夹
441     {
442         wstring folder_path{ m_folder_explore_tree.GetItemPath(m_tree_item_selected) };
443         ShellExecute(NULL, _T("open"), _T("explorer"), folder_path.c_str(), NULL, SW_SHOWNORMAL);
444     }
445 }
446