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