xref: /MusicPlayer2/MusicPlayer2/PlayListCtrl.cpp (revision f3728c8579270583f00105e28c21564840605bd9)
1 // ListCtrlEx.cpp : 实现文件
2 //
3 
4 #include "stdafx.h"
5 #include "MusicPlayer2.h"
6 #include "Player.h"
7 #include "PlayListCtrl.h"
8 #include "SongInfoHelper.h"
9 
10 // CPlayListCtrl
11 
IMPLEMENT_DYNAMIC(CPlayListCtrl,CListCtrlEx)12 IMPLEMENT_DYNAMIC(CPlayListCtrl, CListCtrlEx)
13 
14 //通过构造函数参数传递列表中所有文件的信息的引用
15 CPlayListCtrl::CPlayListCtrl(const vector<SongInfo>& all_song_info) :m_all_song_info{ all_song_info }
16 {
17     m_toolTip.CreateEx(this, TTS_ALWAYSTIP | TTS_NOPREFIX, WS_EX_TRANSPARENT);
18 }
19 
~CPlayListCtrl()20 CPlayListCtrl::~CPlayListCtrl()
21 {
22 }
23 
ShowPlaylist(DisplayFormat display_format,bool search_result)24 void CPlayListCtrl::ShowPlaylist(DisplayFormat display_format, bool search_result)
25 {
26     m_searched = search_result;
27     m_list_data.clear();
28     if (m_all_song_info.size() == 1 && m_all_song_info[0].file_path.empty())
29     {
30         DeleteAllItems();
31         return;
32     }
33 
34     if (!search_result)		//显示所有曲目
35     {
36         int index{};
37         for (const auto& song : m_all_song_info)
38         {
39             CListCtrlEx::RowData row_data;
40             row_data[0] = std::to_wstring(index + 1);
41             row_data[1] = CSongInfoHelper::GetDisplayStr(song, display_format);
42             row_data[2] = song.length().toString();
43             m_list_data.push_back(std::move(row_data));
44             index++;
45         }
46     }
47     else		//只显示搜索结果的曲目
48     {
49         if (m_search_result.empty())
50         {
51             CListCtrlEx::RowData row_data;
52             row_data[0] = wstring();
53             row_data[1] = theApp.m_str_table.LoadText(L"TXT_PLAYLIST_CTRL_NO_RESULT_TO_SHOW");
54             m_list_data.push_back(std::move(row_data));
55 
56         }
57         else
58         {
59             for (int index : m_search_result)
60             {
61                 CListCtrlEx::RowData row_data;
62                 row_data[0] = std::to_wstring(index + 1);
63                 row_data[1] = CSongInfoHelper::GetDisplayStr(m_all_song_info[index], display_format);
64                 row_data[2] = m_all_song_info[index].length().toString();
65                 m_list_data.push_back(std::move(row_data));
66             }
67         }
68     }
69     SetListData(&m_list_data);
70 }
71 
QuickSearch(const wstring & key_word)72 void CPlayListCtrl::QuickSearch(const wstring & key_word)
73 {
74     m_search_result.clear();
75     if (key_word.empty())
76         return;
77     for (size_t i{}; i < m_all_song_info.size(); i++)
78     {
79         if (theApp.m_chinese_pingyin_res.IsStringMatchWithPingyin(key_word, m_all_song_info[i].GetFileName())
80             || theApp.m_chinese_pingyin_res.IsStringMatchWithPingyin(key_word, m_all_song_info[i].title)
81             || theApp.m_chinese_pingyin_res.IsStringMatchWithPingyin(key_word, m_all_song_info[i].artist)
82             || theApp.m_chinese_pingyin_res.IsStringMatchWithPingyin(key_word, m_all_song_info[i].album))
83             m_search_result.push_back(i);
84     }
85 }
86 
GetItemSelectedSearched(vector<int> & item_selected)87 void CPlayListCtrl::GetItemSelectedSearched(vector<int>& item_selected)
88 {
89     item_selected.clear();
90     POSITION pos = GetFirstSelectedItemPosition();
91     if (pos != NULL)
92     {
93         while (pos)
94         {
95             int nItem = GetNextSelectedItem(pos);
96             CString str;
97             str = GetItemText(nItem, 0);
98             item_selected.push_back(_ttoi(str) - 1);
99         }
100     }
101 }
102 
ShowPopupMenu(CMenu * pMenu,int item_index,CWnd * pWnd)103 void CPlayListCtrl::ShowPopupMenu(CMenu* pMenu, int item_index, CWnd* pWnd)
104 {
105     m_toolTip.Pop();
106     CListCtrlEx::ShowPopupMenu(pMenu, item_index, pWnd);
107 }
108 
AdjustColumnWidth()109 void CPlayListCtrl::AdjustColumnWidth()
110 {
111     vector<int> width;
112     CalculateColumeWidth(width);
113 
114     for (size_t i{}; i<width.size(); i++)
115         SetColumnWidth(i, width[i]);
116 }
117 
118 
SetRowHeight(int height)119 bool CPlayListCtrl::SetRowHeight(int height)
120 {
121     //调用基类CListCtrlEx::SetRowHeight函数,指定了列表左侧的空白宽度
122     return CListCtrlEx::SetRowHeight(height, theApp.DPI(6) - 4);
123 }
124 
BEGIN_MESSAGE_MAP(CPlayListCtrl,CListCtrlEx)125 BEGIN_MESSAGE_MAP(CPlayListCtrl, CListCtrlEx)
126     ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, CPlayListCtrl::OnNMCustomdraw)
127     ON_WM_MOUSEMOVE()
128     ON_WM_LBUTTONDOWN()
129     ON_WM_RBUTTONDOWN()
130     ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CListCtrlEx::OnNMCustomdraw)
131 END_MESSAGE_MAP()
132 
133 
134 // CPlayListCtrl 消息处理程序
135 
136 void CPlayListCtrl::CalculateColumeWidth(vector<int>& width)
137 {
138     width.resize(3);
139 
140     width[0] = theApp.DPI(40);
141     width[2] = theApp.DPI(50);
142     CRect rect;
143     GetWindowRect(rect);
144     width[1] = rect.Width() - width[0] - width[2] - theApp.DPI(20) - 1;
145 }
146 
OnMouseMove(UINT nFlags,CPoint point)147 void CPlayListCtrl::OnMouseMove(UINT nFlags, CPoint point)
148 {
149     // TODO: 在此添加消息处理程序代码和/或调用默认值
150     //如果开启文本提示
151     if (theApp.m_media_lib_setting_data.show_playlist_tooltip)
152     {
153         LVHITTESTINFO lvhti;
154 
155         // 判断鼠标当前所在的位置(行, 列)
156         lvhti.pt = point;
157         SubItemHitTest(&lvhti);
158 
159         //如果鼠标移动到另一行, 则进行处理; 否则, 不做处理
160         if (lvhti.iItem != m_nItem)
161         {
162             // 保存当前鼠标所在的行
163             m_nItem = lvhti.iItem;
164 
165             // 如果鼠标移动到一个合法的行,则显示新的提示信息,否则不显示提示
166             if (m_nItem >= 0 && m_nItem < static_cast<int>(m_all_song_info.size()) && !m_dragging)
167             {
168                 int song_index;
169                 if (!m_searched)
170                 {
171                     song_index = m_nItem;
172                 }
173                 else
174                 {
175                     CString str = GetItemText(m_nItem, 0);
176                     song_index = _ttoi(str) - 1;
177                 }
178                 if (song_index < 0 || song_index >= static_cast<int>(m_all_song_info.size()))
179                     return;
180 
181                 CString dis_str = GetItemText(m_nItem, 1);
182                 int strWidth = GetStringWidth(dis_str) + theApp.DPI(10);		//获取要显示当前字符串的最小宽度
183                 int columnWidth = GetColumnWidth(1);	//获取鼠标指向列的宽度
184                 bool show_title = (columnWidth < strWidth);		//当单元格内的的字符无法显示完全时在提示的第1行显示单元格内文本
185                 bool show_full_path = (!CPlayer::GetInstance().IsFolderMode() || CPlayer::GetInstance().IsContainSubFolder());
186                 CString str_tip = CSongInfoHelper::GetPlaylistItemToolTip(m_all_song_info[song_index], show_title, show_full_path).c_str();
187 
188                 m_toolTip.SetMaxTipWidth(theApp.DPI(400));		//设置提示信息的宽度,以支持提示换行
189 
190                 m_toolTip.AddTool(this, str_tip);
191                 m_toolTip.Pop();			// 显示提示框
192             }
193             else
194             {
195                 m_toolTip.AddTool(this, _T(""));
196                 m_toolTip.Pop();
197             }
198         }
199     }
200     else
201     {
202         m_toolTip.AddTool(this, _T(""));
203         m_toolTip.Pop();
204     }
205     CListCtrlEx::OnMouseMove(nFlags, point);
206 }
207 
208 
PreTranslateMessage(MSG * pMsg)209 BOOL CPlayListCtrl::PreTranslateMessage(MSG* pMsg)
210 {
211     // TODO: 在此添加专用代码和/或调用基类
212     if (m_toolTip.GetSafeHwnd() && pMsg->message == WM_MOUSEMOVE)
213     {
214         m_toolTip.RelayEvent(pMsg);
215     }
216 
217     return CListCtrlEx::PreTranslateMessage(pMsg);
218 }
219 
220 
PreSubclassWindow()221 void CPlayListCtrl::PreSubclassWindow()
222 {
223     // TODO: 在此添加专用代码和/或调用基类
224     //CWindowDC dc(this);
225     //HDC hDC = dc.GetSafeHdc();
226     //m_dpi = GetDeviceCaps(hDC, LOGPIXELSY);
227 
228     CListCtrlEx::PreSubclassWindow();
229 
230     //将提示信息设为置顶
231     m_toolTip.SetWindowPos(&CWnd::wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
232 
233     //初始化播放列表
234     DWORD style = GetExtendedStyle();
235     style = (style | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
236     style &= ~LVS_EX_LABELTIP;      //播放列表控件使用自己的鼠标提示,因此不需要LVS_EX_LABELTIP样式
237     SetExtendedStyle(style);
238     vector<int> width;
239     CalculateColumeWidth(width);
240     InsertColumn(0, theApp.m_str_table.LoadText(L"TXT_SERIAL_NUMBER").c_str(), LVCFMT_LEFT, width[0]);
241     InsertColumn(1, theApp.m_str_table.LoadText(L"TXT_TRACK").c_str(), LVCFMT_LEFT, width[1]);
242     InsertColumn(2, theApp.m_str_table.LoadText(L"TXT_LENGTH").c_str(), LVCFMT_LEFT, width[2]);
243     SetCtrlAEnable(true);
244 
245     SetRowHeight(theApp.DPI(theApp.m_media_lib_setting_data.playlist_item_height));
246 
247 }
248 
249 
OnNMCustomdraw(NMHDR * pNMHDR,LRESULT * pResult)250 void CPlayListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
251 {
252     *pResult = CDRF_DODEFAULT;
253     LPNMLVCUSTOMDRAW lplvdr = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
254     NMCUSTOMDRAW& nmcd = lplvdr->nmcd;
255     static bool this_item_select = false;
256     switch (lplvdr->nmcd.dwDrawStage)	//判断状态
257     {
258     case CDDS_PREPAINT:
259         *pResult = CDRF_NOTIFYITEMDRAW;
260         break;
261     case CDDS_ITEMPREPAINT:			//如果为画ITEM之前就要进行颜色的改变
262         if (IsWindowEnabled())
263         {
264             this_item_select = false;
265             int highlight_item{ m_highlight_item };
266             if (m_searched && m_search_result.size() == 0)		//如果播放列表处于搜索状态且没有搜索结果
267             {
268                 if (GetItemState(nmcd.dwItemSpec, LVIS_SELECTED) == LVIS_SELECTED)	//不允许选中行
269                     SetItemState(nmcd.dwItemSpec, 0, LVIS_SELECTED);
270                 lplvdr->clrText = GRAY(140);
271                 lplvdr->clrTextBk = GRAY(255);
272             }
273             else
274             {
275                 if (!m_searched || m_search_result.size() == m_all_song_info.size())	//当播放列表不处理搜索状态,或搜索结果数量等于播放列表中曲目数量时
276                 {
277                     highlight_item = m_highlight_item;
278                 }
279                 else		//如果播放列表处于搜索状态,则高亮项目应该为搜索结果的索引
280                 {
281                     auto iter = std::find(m_search_result.begin(), m_search_result.end(), m_highlight_item);
282                     if (iter == m_search_result.end())
283                         highlight_item = -1;
284                     else
285                         highlight_item = iter - m_search_result.begin();
286                 }
287                 //当选中行又是高亮行时设置颜色
288                 if (GetItemState(nmcd.dwItemSpec, LVIS_SELECTED) == LVIS_SELECTED && nmcd.dwItemSpec == highlight_item)
289                 {
290                     this_item_select = true;
291                     //SetItemState(nmcd.dwItemSpec, 0, LVIS_SELECTED);
292                     lplvdr->clrText = m_theme_color.light3;
293                     lplvdr->clrTextBk = m_theme_color.dark1;
294                 }
295                 //设置选中行的颜色
296                 else if (GetItemState(nmcd.dwItemSpec, LVIS_SELECTED) == LVIS_SELECTED)
297                 {
298                     this_item_select = true;
299                     //SetItemState(nmcd.dwItemSpec, 0, LVIS_SELECTED);
300                     lplvdr->clrText = m_theme_color.dark3;
301                     lplvdr->clrTextBk = m_theme_color.light2;
302                 }
303                 //设置高亮行的颜色
304                 else if (nmcd.dwItemSpec == highlight_item)
305                 {
306                     lplvdr->clrText = m_theme_color.dark2;
307                     //lplvdr->clrText = 0;
308                     lplvdr->clrTextBk = m_theme_color.light3;
309                 }
310                 //设置偶数行的颜色
311                 else if (nmcd.dwItemSpec % 2 == 0)
312                 {
313                     lplvdr->clrText = CColorConvert::m_gray_color.dark3;
314                     lplvdr->clrTextBk = CColorConvert::m_gray_color.light3;
315                 }
316                 //设置奇数行的颜色
317                 else
318                 {
319                     lplvdr->clrText = CColorConvert::m_gray_color.dark3;
320                     lplvdr->clrTextBk = CColorConvert::m_gray_color.light4;
321                 }
322             }
323 
324             //用背景色填充单元格,以去掉每行前面的空白
325             CRect rect = nmcd.rc;
326             CDC* pDC = CDC::FromHandle(nmcd.hdc);		//获取绘图DC
327             COLORREF left_color{};
328             left_color = lplvdr->clrTextBk;
329             pDC->FillSolidRect(rect, left_color);
330             //如果是高亮(正在播放)行,则在左侧绘制一个表示高亮的矩形
331             bool is_search_result_empty = (m_searched && m_search_result.empty());
332             if (nmcd.dwItemSpec == highlight_item && !is_search_result_empty)
333             {
334                 CRect highlight_rect = rect;
335                 highlight_rect.right = highlight_rect.left + theApp.DPI(4);
336                 int hight = rect.Height() * 7 / 10;
337                 highlight_rect.top += (rect.Height() - hight) / 2;
338                 highlight_rect.bottom = highlight_rect.top + hight;
339                 pDC->FillSolidRect(highlight_rect, m_theme_color.dark1);
340             }
341         }
342         else		//当控件被禁用时,显示文本设为灰色
343         {
344             lplvdr->clrText = GRAY(140);
345             lplvdr->clrTextBk = GRAY(240);
346         }
347         *pResult = CDRF_NOTIFYPOSTPAINT;
348         break;
349     case CDDS_ITEMPOSTPAINT:
350         if (this_item_select)
351             SetItemState(nmcd.dwItemSpec, 0xFF, LVIS_SELECTED);
352         //*pResult = CDRF_DODEFAULT;
353         break;
354     }
355 }
356