1 #include "stdafx.h"
2 #include "ListCtrlEx.h"
3 #include "MusicPlayer2.h"
4
IMPLEMENT_DYNAMIC(CListCtrlEx,CListCtrl)5 IMPLEMENT_DYNAMIC(CListCtrlEx, CListCtrl)
6
7 CListCtrlEx::CListCtrlEx()
8 : m_theme_color(theApp.m_app_setting_data.theme_color)
9 {
10
11 m_drag_cursor = AfxGetApp()->LoadCursor(IDC_DRAG_CURSOR);
12 }
13
14
~CListCtrlEx()15 CListCtrlEx::~CListCtrlEx()
16 {
17 DestroyCursor(m_drag_cursor);
18 }
19
20
21 //void CListCtrlEx::SetColor(const ColorTable & colors)
22 //{
23 // m_theme_color = colors;
24 // if (m_hWnd != NULL)
25 // Invalidate();
26 //}
27
GetItemSelected(vector<int> & item_selected) const28 void CListCtrlEx::GetItemSelected(vector<int>& item_selected) const
29 {
30 item_selected.clear();
31 POSITION pos = GetFirstSelectedItemPosition();
32 if (pos != NULL)
33 {
34 while (pos)
35 {
36 int nItem = GetNextSelectedItem(pos);
37 item_selected.push_back(nItem);
38 }
39 }
40 }
41
GetCurSel() const42 int CListCtrlEx::GetCurSel() const
43 {
44 //vector<int> item_selected;
45 //GetItemSelected(item_selected);
46 //if (!item_selected.empty())
47 // return item_selected[0];
48 //else
49 // return -1;
50 return GetSelectionMark();
51 }
52
SetCurSel(int select)53 void CListCtrlEx::SetCurSel(int select)
54 {
55 int size = GetItemCount();
56 for (int i{}; i < size; i++)
57 {
58 if (i == select)
59 {
60 SetItemState(select, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); //选中行
61 EnsureVisible(select, FALSE); //使选中行保持可见
62 }
63 else
64 {
65 SetItemState(i, 0, LVIS_SELECTED); //取消选中其他行
66 }
67 }
68 }
69
SetCurSel(int first,int last)70 void CListCtrlEx::SetCurSel(int first, int last)
71 {
72 int itemCnt = GetItemCount();
73 if (first >= 0 && last < itemCnt && first <= last)
74 {
75 for (int i = 0; i < itemCnt; i++)
76 {
77 if(i>=first && i<=last)
78 SetItemState(i, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
79 else
80 SetItemState(i, 0, LVIS_SELECTED);
81 }
82 }
83 }
84
SetCurSel(const vector<int> indexes)85 void CListCtrlEx::SetCurSel(const vector<int> indexes)
86 {
87 int itemCnt = GetItemCount();
88 for (int i = 0; i < itemCnt; i++)
89 {
90 auto iter = std::find(indexes.begin(), indexes.end(), i);
91 if (iter != indexes.end())
92 SetItemState(i, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
93 else
94 SetItemState(i, 0, LVIS_SELECTED);
95 }
96 }
97
SelectAll()98 void CListCtrlEx::SelectAll()
99 {
100 int itemCnt = GetItemCount();
101 for (int i = 0; i < itemCnt; i++)
102 {
103 SetItemState(i, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
104 }
105 }
106
SelectNone()107 void CListCtrlEx::SelectNone()
108 {
109 int itemCnt = GetItemCount();
110 for (int i = 0; i < itemCnt; i++)
111 {
112 SetItemState(i, 0, LVIS_SELECTED);
113 }
114 }
115
SelectReverse()116 void CListCtrlEx::SelectReverse()
117 {
118 std::vector<int> selected_vect;
119 GetItemSelected(selected_vect);
120 std::set<int> selected_set;
121 for (auto n : selected_vect)
122 selected_set.insert(n);
123 int size = GetItemCount();
124 for (int i = 0; i < size; i++)
125 {
126 if(selected_set.find(i)==selected_set.end())
127 SetItemState(i, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
128 else
129 SetItemState(i, 0, LVIS_SELECTED);
130 }
131 }
132
SetRowHeight(int height,int left_space)133 bool CListCtrlEx::SetRowHeight(int height, int left_space)
134 {
135 if (height > 0 && height <= 512)
136 {
137 CImageList imgList; //为ClistCtrl设置一个图像列表,以设置行高
138 int width = left_space;
139 if (width < 1)
140 width = 1;
141 BOOL rtn = imgList.Create(width, height, ILC_COLOR, 1, 1);
142 if (rtn != FALSE)
143 {
144 m_row_height = height;
145 SetImageList(&imgList, LVSIL_SMALL);
146 return true;
147 }
148 }
149 return false;
150 }
151
152
ShowPopupMenu(CMenu * pMenu,int item_index,CWnd * pWnd)153 void CListCtrlEx::ShowPopupMenu(CMenu* pMenu, int item_index, CWnd* pWnd)
154 {
155 CPoint point; //定义一个用于确定光标位置的位置
156 GetCursorPos(&point); //获取当前光标的位置,以便使得菜单可以跟随光标
157 if(item_index >= 0)
158 {
159 CRect item_rect;
160 GetItemRect(item_index, item_rect, LVIR_BOUNDS); //获取选中项目的矩形区域(以列表控件左上角为原点)
161 CRect window_rect;
162 GetWindowRect(window_rect); //获取列表控件的矩形区域(以屏幕左上角为原点)
163 point.y = window_rect.top + item_rect.bottom; //设置鼠标要弹出的y坐标为选中项目的下边框位置,防止右键菜单挡住选中的项目
164 }
165 pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, pWnd); //在指定位置显示弹出菜单
166 }
167
FillLeftSpaceAfterPaint(bool fill)168 void CListCtrlEx::FillLeftSpaceAfterPaint(bool fill)
169 {
170 m_fill_left_space_after_paint = fill;
171 }
172
SetMouseWheelEnable(bool enable)173 void CListCtrlEx::SetMouseWheelEnable(bool enable)
174 {
175 m_mouse_wheel_enable = enable;
176 }
177
SetItemIcon(int item,HICON icon)178 void CListCtrlEx::SetItemIcon(int item, HICON icon)
179 {
180 m_icons[item] = icon;
181 m_fill_left_space_after_paint = false; //设置了图标后将m_fill_left_space_after_paint置为false,以确保图标部分背景显示正常
182 }
183
DeleteItem(int nItem)184 bool CListCtrlEx::DeleteItem(int nItem)
185 {
186 bool rtn = (CListCtrl::DeleteItem(nItem) != FALSE);
187
188 if (rtn)
189 {
190 //删除对应的图标
191 m_icons.erase(nItem);
192
193 //删除项目后要重新调整删除项目后面所有图标的序号
194 for (int i = nItem + 1; i <= GetItemCount(); i++)
195 {
196 auto iter = m_icons.find(i);
197 if (iter != m_icons.end())
198 {
199 HICON icon = iter->second;
200 m_icons.erase(iter);
201 m_icons[i - 1] = icon;
202 }
203 }
204
205 Invalidate(); //控件重绘
206 }
207
208 return rtn;
209 }
210
SetListData(ListData * pListData)211 void CListCtrlEx::SetListData(ListData* pListData)
212 {
213 if (pListData == nullptr)
214 return;
215 m_pListData = pListData;
216 SetItemCount(pListData->size());
217 }
218
SetListData(const ListData & list_data)219 void CListCtrlEx::SetListData(const ListData & list_data)
220 {
221 m_pListData = nullptr;
222 int item_num_before = GetItemCount();
223 int item_num_after = list_data.size();
224 //如果当前列表中项目的数量小于原来的,则直接清空原来列表中所有的项目,重新添加
225 if (item_num_after < item_num_before)
226 {
227 DeleteAllItems();
228 item_num_before = 0;
229 }
230 for (int i{}; i < item_num_after; i++)
231 {
232 const RowData& data_row = list_data[i];
233 if (i >= item_num_before) //如果当前列表中的项目数量大于之前的数量,则需要在不够时插入新的项目
234 {
235 auto iter = data_row.find(0);
236 if (iter != data_row.end())
237 InsertItem(i, iter->second.c_str());
238 else
239 InsertItem(i, _T(""));
240 }
241 for (const auto& item : data_row)
242 {
243 SetItemText(i, item.first, item.second.c_str());
244 }
245 }
246 }
247
GetAllText(const wchar_t * sperator)248 wstring CListCtrlEx::GetAllText(const wchar_t* sperator /*= L"\t"*/)
249 {
250 wstring str_result;
251 int item_count = GetItemCount();
252 int col_count{};
253 auto pHeader = GetHeaderCtrl();
254 if (pHeader != nullptr)
255 col_count = pHeader->GetItemCount();
256 for (int i = 0; i < item_count; i++)
257 {
258 for (int j = 0; j < col_count; j++)
259 {
260 str_result += GetItemText(i, j).GetString();
261 if (j < col_count - 1)
262 str_result += sperator;
263 }
264 str_result += L"\r\n";
265 }
266 return str_result;
267 }
268
BEGIN_MESSAGE_MAP(CListCtrlEx,CListCtrl)269 BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
270 ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CListCtrlEx::OnNMCustomdraw)
271 ON_WM_LBUTTONDOWN()
272 ON_WM_RBUTTONDOWN()
273 ON_WM_SETFOCUS()
274 ON_NOTIFY_REFLECT(LVN_BEGINDRAG, &CListCtrlEx::OnLvnBegindrag)
275 ON_WM_LBUTTONUP()
276 ON_WM_SETCURSOR()
277 ON_WM_ERASEBKGND()
278 ON_NOTIFY_REFLECT(LVN_GETDISPINFO, &CListCtrlEx::OnLvnGetdispinfo)
279 ON_MESSAGE(WM_TABLET_QUERYSYSTEMGESTURESTATUS, &CListCtrlEx::OnTabletQuerysystemgesturestatus)
280 END_MESSAGE_MAP()
281
282
283 void CListCtrlEx::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
284 {
285 DWORD style = GetExtendedStyle();
286
287
288 *pResult = CDRF_DODEFAULT;
289 LPNMLVCUSTOMDRAW lplvdr = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
290 NMCUSTOMDRAW& nmcd = lplvdr->nmcd;
291 static bool this_item_select = false;
292 switch (lplvdr->nmcd.dwDrawStage) //判断状态
293 {
294 case CDDS_PREPAINT:
295 *pResult = CDRF_NOTIFYITEMDRAW;
296 break;
297 case CDDS_ITEMPREPAINT: //如果为画ITEM之前就要进行颜色的改变
298 this_item_select = false;
299 if (IsWindowEnabled())
300 {
301 //当选中行又是高亮行时设置颜色
302 if (GetItemState(nmcd.dwItemSpec, LVIS_SELECTED) == LVIS_SELECTED && nmcd.dwItemSpec == m_highlight_item)
303 {
304 this_item_select = true;
305 //SetItemState(nmcd.dwItemSpec, 0, LVIS_SELECTED);
306 lplvdr->clrText = m_theme_color.light3;
307 lplvdr->clrTextBk = m_theme_color.dark1;
308 }
309 //设置选中行的颜色
310 else if (GetItemState(nmcd.dwItemSpec, LVIS_SELECTED) == LVIS_SELECTED)
311 {
312 this_item_select = true;
313 //注:当使用虚拟列表时,如果取消下面一行的注释,会导致当列表选中行处理可见状态时,窗口刷新不正常,甚至主窗口OnTimer也无法响应,原因暂时不明
314 //SetItemState(nmcd.dwItemSpec, 0, LVIS_SELECTED);
315 lplvdr->clrText = m_theme_color.dark3;
316 lplvdr->clrTextBk = m_theme_color.light2;
317 }
318 //设置高亮行的颜色
319 else if (nmcd.dwItemSpec == m_highlight_item)
320 {
321 lplvdr->clrText = m_theme_color.dark2;
322 //lplvdr->clrText = 0;
323 lplvdr->clrTextBk = m_theme_color.light3;
324 }
325 //设置偶数行的颜色
326 else if (nmcd.dwItemSpec % 2 == 0)
327 {
328 lplvdr->clrText = CColorConvert::m_gray_color.dark3;
329 lplvdr->clrTextBk = CColorConvert::m_gray_color.light3;
330 }
331 //设置奇数行的颜色
332 else
333 {
334 lplvdr->clrText = CColorConvert::m_gray_color.dark3;
335 lplvdr->clrTextBk = CColorConvert::m_gray_color.light4;
336 }
337
338 //用背景色填充单元格,以去掉每行前面的空白
339 if(!m_fill_left_space_after_paint)
340 {
341 CRect rect = nmcd.rc;
342 CDC* pDC = CDC::FromHandle(nmcd.hdc); //获取绘图DC
343 pDC->FillSolidRect(rect, lplvdr->clrTextBk);
344 }
345 }
346 else //当控件被禁用时,显示文本设为灰色
347 {
348 lplvdr->clrText = GRAY(140);
349 lplvdr->clrTextBk = GRAY(240);
350 }
351 *pResult = CDRF_NOTIFYPOSTPAINT;
352 break;
353 case CDDS_ITEMPOSTPAINT:
354 if (this_item_select)
355 SetItemState(nmcd.dwItemSpec, 0xFF, LVIS_SELECTED);
356 //用背景色填充单元格左侧的空白区域
357 if(m_fill_left_space_after_paint)
358 {
359 CRect rect = nmcd.rc;
360 rect.right = rect.left + 5;
361 CDC* pDC = CDC::FromHandle(nmcd.hdc); //获取绘图DC
362 pDC->FillSolidRect(rect, lplvdr->clrTextBk);
363 }
364 //绘制图标
365 CRect icon_rect = nmcd.rc;
366 if (!icon_rect.IsRectEmpty())
367 {
368 auto iter = m_icons.find(nmcd.dwItemSpec);
369 if (iter != m_icons.end())
370 {
371 icon_rect.left += 5;
372 const int icon_size{ theApp.DPI(16) };
373 icon_rect.right = icon_rect.left + icon_size;
374 icon_rect.top = icon_rect.top + (icon_rect.Height() - icon_size) / 2;
375 icon_rect.bottom = icon_rect.top + icon_size;
376 CDC* pDC = CDC::FromHandle(nmcd.hdc);
377 CDrawCommon drawer;
378 drawer.Create(pDC);
379 drawer.DrawIcon(iter->second, icon_rect);
380 }
381 }
382
383 //*pResult = CDRF_DODEFAULT;
384 break;
385 }
386 }
387
388
PreSubclassWindow()389 void CListCtrlEx::PreSubclassWindow()
390 {
391 // TODO: 在此添加专用代码和/或调用基类
392 CListCtrl::PreSubclassWindow();
393
394 DWORD style = GetExtendedStyle();
395 SetExtendedStyle(style | LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); //设置扩展样式(双缓冲绘图、整行选中,鼠标提示)
396
397 SetBkColor(m_background_color);
398 //SetHightItem(-1);
399 SetRowHeight(theApp.DPI(22));
400 }
401
402
OnLButtonDown(UINT nFlags,CPoint point)403 void CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
404 {
405 // TODO: 在此添加消息处理程序代码和/或调用默认值
406 this->SetFocus();
407 m_dragging = false;
408 CListCtrl::OnLButtonDown(nFlags, point);
409 }
410
411
OnRButtonDown(UINT nFlags,CPoint point)412 void CListCtrlEx::OnRButtonDown(UINT nFlags, CPoint point)
413 {
414 // TODO: 在此添加消息处理程序代码和/或调用默认值
415 this->SetFocus();
416 m_dragging = false;
417 CListCtrl::OnRButtonDown(nFlags, point);
418 }
419
420
PreTranslateMessage(MSG * pMsg)421 BOOL CListCtrlEx::PreTranslateMessage(MSG* pMsg)
422 {
423 // TODO: 在此添加专用代码和/或调用基类
424
425 //if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_CHAR) //屏蔽列表控件的键盘消息,防止每次按下一个键时列表选中行会出现讨厌的、难看的虚线框
426 // return TRUE;
427
428 // 按Ctrl+A全选,向父窗口发送选中项更新消息
429 if(m_enable_ctrl_a)
430 {
431 if ((GetKeyState(VK_CONTROL) & 0x80) && (pMsg->wParam == 'A'))
432 {
433 SelectAll();
434 CWnd* pParent{ GetParent() };
435 if (pParent != nullptr)
436 {
437 pParent->SendMessage(WM_COMMAND, ID_PLAYLIST_SELECT_CHANGE);
438 }
439 return TRUE;
440 }
441 }
442
443 if (pMsg->message == WM_MOUSEWHEEL && !m_mouse_wheel_enable)
444 {
445 CRect rect;
446 GetWindowRect(rect);
447 //根据行高*行数小于控件高度来判断是否显示垂直滚动条
448 if (m_row_height > 0 && (m_row_height + 1) * GetItemCount() < rect.Height())
449 {
450 //如果不显示滚动条,则将鼠标滚轮消息发送给父窗口
451 CWnd* pParent = GetParent();
452 pParent->SendMessage(WM_MOUSEWHEEL, pMsg->wParam, pMsg->lParam);
453 return TRUE;
454 }
455 }
456
457 return CListCtrl::PreTranslateMessage(pMsg);
458 }
459
460
OnSetFocus(CWnd * pOldWnd)461 void CListCtrlEx::OnSetFocus(CWnd* pOldWnd)
462 {
463 CListCtrl::OnSetFocus(pOldWnd);
464
465 // TODO: 在此处添加消息处理程序代码
466
467 SendMessage(WM_KILLFOCUS); //禁止列表控件获取焦点,防止选中行会出现难看的虚线框
468 }
469
470
OnLvnBegindrag(NMHDR * pNMHDR,LRESULT * pResult)471 void CListCtrlEx::OnLvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)
472 {
473 LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
474 // TODO: 在此添加控件通知处理程序代码
475 if (m_drag_enable)
476 {
477 m_dragging = true;
478 }
479
480 *pResult = 0;
481 }
482
483
OnLButtonUp(UINT nFlags,CPoint point)484 void CListCtrlEx::OnLButtonUp(UINT nFlags, CPoint point)
485 {
486 // TODO: 在此添加消息处理程序代码和/或调用默认值
487 if (m_dragging)
488 {
489 CPoint pt(point);
490 int drop_index = this->HitTest(pt); //鼠标松开时的项目序号
491 CWnd* pParent{ GetParent() };
492 if (pParent != nullptr)
493 {
494 pParent->SendMessage(WM_LIST_ITEM_DRAGGED, drop_index, 0); //结束拖放时向父窗口发送消息,传递拖放结束位置索引
495 }
496 m_dragging = false;
497 SendMessage(WM_SETCURSOR); //鼠标松开时刷新光标
498 }
499
500 CListCtrl::OnLButtonUp(nFlags, point);
501 }
502
503
OnSetCursor(CWnd * pWnd,UINT nHitTest,UINT message)504 BOOL CListCtrlEx::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
505 {
506 // TODO: 在此添加消息处理程序代码和/或调用默认值
507 if (m_dragging)
508 {
509 ::SetCursor(m_drag_cursor);
510 return TRUE;
511 }
512
513 return CListCtrl::OnSetCursor(pWnd, nHitTest, message);
514 }
515
516
OnEraseBkgnd(CDC * pDC)517 BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC)
518 {
519 // TODO: 在此添加消息处理程序代码和/或调用默认值
520
521 //return CListCtrl::OnEraseBkgnd(pDC);
522 return TRUE;
523 }
524
525
OnLvnGetdispinfo(NMHDR * pNMHDR,LRESULT * pResult)526 void CListCtrlEx::OnLvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult)
527 {
528 NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
529 // TODO: 在此添加控件通知处理程序代码
530 if (pDispInfo->hdr.hwndFrom == m_hWnd && m_pListData != nullptr)
531 {
532 if (pDispInfo->item.iItem >= 0 && pDispInfo->item.iItem < static_cast<int>(m_pListData->size()))
533 {
534 auto& row_data{ m_pListData->at(pDispInfo->item.iItem) };
535 auto iter = row_data.find(pDispInfo->item.iSubItem);
536 if (iter != row_data.end())
537 CCommon::WStringCopy(pDispInfo->item.pszText, pDispInfo->item.cchTextMax, iter->second.c_str());
538 }
539 }
540 *pResult = 0;
541 }
542
543
OnTabletQuerysystemgesturestatus(WPARAM wParam,LPARAM lParam)544 afx_msg LRESULT CListCtrlEx::OnTabletQuerysystemgesturestatus(WPARAM wParam, LPARAM lParam)
545 {
546 return 0;
547 }
548