xref: /MusicPlayer2/MusicPlayer2/CListenTimeStatisticsDlg.cpp (revision 52e916bf950c2adf540da0d33403a4ded9f9447d)
1 // CListenTimeStatisticsDlg.cpp: 实现文件
2 //
3 
4 #include "stdafx.h"
5 #include "MusicPlayer2.h"
6 #include "CListenTimeStatisticsDlg.h"
7 #include "SongDataManager.h"
8 #include "FilterHelper.h"
9 #include "AudioCommon.h"
10 
11 
12 // CListenTimeStatisticsDlg 对话框
13 
IMPLEMENT_DYNAMIC(CListenTimeStatisticsDlg,CBaseDialog)14 IMPLEMENT_DYNAMIC(CListenTimeStatisticsDlg, CBaseDialog)
15 
16 CListenTimeStatisticsDlg::CListenTimeStatisticsDlg(CWnd* pParent /*=nullptr*/)
17     : CBaseDialog(IDD_LISTEN_TIME_STATISTICS_DLG, pParent)
18 {
19 
20 }
21 
~CListenTimeStatisticsDlg()22 CListenTimeStatisticsDlg::~CListenTimeStatisticsDlg()
23 {
24 }
25 
GetDialogName() const26 CString CListenTimeStatisticsDlg::GetDialogName() const
27 {
28     return L"ListenTimeStatisticsDlg";
29 }
30 
InitializeControls()31 bool CListenTimeStatisticsDlg::InitializeControls()
32 {
33     wstring temp;
34     temp = theApp.m_str_table.LoadText(L"TITLE_LISTEN_TIME");
35     SetWindowTextW(temp.c_str());
36     // IDC_LIST1
37     temp = theApp.m_str_table.LoadText(L"TXT_LISTEN_TIME_EXPORT");
38     SetDlgItemTextW(IDC_EXPORT_BUTTON, temp.c_str());
39     temp = theApp.m_str_table.LoadText(L"TXT_LISTEN_TIME_CLEAR");
40     SetDlgItemTextW(IDC_CLEAR_BUTTON, temp.c_str());
41     temp = theApp.m_str_table.LoadText(L"TXT_CLOSE");
42     SetDlgItemTextW(IDCANCEL, temp.c_str());
43 
44     RepositionTextBasedControls({
45         { CtrlTextInfo::L2, IDC_EXPORT_BUTTON, CtrlTextInfo::W32 },
46         { CtrlTextInfo::L1, IDC_CLEAR_BUTTON, CtrlTextInfo::W32 },
47         { CtrlTextInfo::R1, IDCANCEL, CtrlTextInfo::W32 }
48         });
49     return true;
50 }
51 
DoDataExchange(CDataExchange * pDX)52 void CListenTimeStatisticsDlg::DoDataExchange(CDataExchange* pDX)
53 {
54     CBaseDialog::DoDataExchange(pDX);
55 	DDX_Control(pDX, IDC_LIST1, m_list_ctrl);
56 }
57 
58 
ShowData(bool size_changed)59 void CListenTimeStatisticsDlg::ShowData(bool size_changed)
60 {
61 	//将vector中的数据显示到列表中
62     if(size_changed)
63         m_list_ctrl.DeleteAllItems();
64 
65 	int index = 0;
66 	for (const auto& data : m_data_list)
67 	{
68         if (size_changed)
69             m_list_ctrl.InsertItem(index, std::to_wstring(index + 1).c_str());
70         else
71             m_list_ctrl.SetItemText(index, COL_INDEX, std::to_wstring(index + 1).c_str());
72 
73 		m_list_ctrl.SetItemText(index, COL_TRACK, data.name.c_str());
74 		m_list_ctrl.SetItemText(index, COL_PATH, data.path.c_str());
75 		m_list_ctrl.SetItemText(index, COL_TOTAL_TIME, data.total_time.toString3().c_str());
76 		m_list_ctrl.SetItemText(index, COL_LENGTH, data.length.toString().c_str());
77         CString str;
78 		str.Format(_T("%.1f"), data.times);
79 		if (str.Right(2) == _T(".0"))
80 			str = str.Left(str.GetLength() - 2);
81 
82 		m_list_ctrl.SetItemText(index, COL_TIMES, str);
83         // 这里之前设置当前播放高亮,意义不大,去掉了
84 		index++;
85 	}
86 }
87 
88 
SongInfoToListItem(const SongInfo & song)89 CListenTimeStatisticsDlg::ListItem CListenTimeStatisticsDlg::SongInfoToListItem(const SongInfo& song)
90 {
91     ListItem list_item;
92     if (song.IsTitleEmpty() || CAudioCommon::GetAudioTypeByFileName(song.file_path) == AU_MIDI)
93     {
94         list_item.name = song.GetFileName().c_str();
95     }
96     else
97     {
98         list_item.name = (song.GetArtist() + L" - " + song.GetTitle()).c_str();
99     }
100 
101     list_item.path = song.file_path;
102     list_item.total_time = Time(song.listen_time * 1000);
103     list_item.length = song.length();
104     list_item.times = static_cast<double>(song.listen_time) / song.length().toInt() * 1000;
105     return list_item;
106 }
107 
BEGIN_MESSAGE_MAP(CListenTimeStatisticsDlg,CBaseDialog)108 BEGIN_MESSAGE_MAP(CListenTimeStatisticsDlg, CBaseDialog)
109 	ON_BN_CLICKED(IDC_EXPORT_BUTTON, &CListenTimeStatisticsDlg::OnBnClickedExportButton)
110 	ON_WM_GETMINMAXINFO()
111     ON_BN_CLICKED(IDC_CLEAR_BUTTON, &CListenTimeStatisticsDlg::OnBnClickedClearButton)
112     ON_NOTIFY(HDN_ITEMCLICK, 0, &CListenTimeStatisticsDlg::OnHdnItemclickList1)
113 END_MESSAGE_MAP()
114 
115 
116 // CListenTimeStatisticsDlg 消息处理程序
117 
118 
119 BOOL CListenTimeStatisticsDlg::OnInitDialog()
120 {
121     CBaseDialog::OnInitDialog();
122 
123 	// TODO:  在此添加额外的初始化
124 
125 	//初始化列表
126     CRect rect;
127     m_list_ctrl.GetWindowRect(rect);
128 	int width[6];
129 	width[0] = theApp.DPI(40);
130 	width[1] = theApp.DPI(150);
131 	width[3] = width[5] = theApp.DPI(60);
132 	width[4] = theApp.DPI(50);
133 	width[2] = rect.Width() - width[1] - width[3] - width[4] - width[5] - width[0] - theApp.DPI(20) - 1;
134     m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_LABELTIP);
135     m_list_ctrl.InsertColumn(0, theApp.m_str_table.LoadText(L"TXT_SERIAL_NUMBER").c_str(), LVCFMT_LEFT, width[0]);
136     m_list_ctrl.InsertColumn(1, theApp.m_str_table.LoadText(L"TXT_TRACK").c_str(), LVCFMT_LEFT, width[1]);
137     m_list_ctrl.InsertColumn(2, theApp.m_str_table.LoadText(L"TXT_PATH").c_str(), LVCFMT_LEFT, width[2]);
138     m_list_ctrl.InsertColumn(3, theApp.m_str_table.LoadText(L"TXT_LISTEN_TIME_TOTAL_TIME").c_str(), LVCFMT_LEFT, width[3]);
139     m_list_ctrl.InsertColumn(4, theApp.m_str_table.LoadText(L"TXT_LENGTH").c_str(), LVCFMT_LEFT, width[4]);
140     m_list_ctrl.InsertColumn(5, theApp.m_str_table.LoadText(L"TXT_LISTEN_TIME_TOTAL_COUNT").c_str(), LVCFMT_LEFT, width[5]);
141 
142     //获取数据
143     //从所有歌曲信息中查找累计听的时间超过指定时间的曲目添加到vector
144     CSongDataManager::GetInstance().GetSongData([&](const CSongDataManager::SongDataMap& song_data_map)
145         {
146             for (const auto& data : song_data_map)
147             {
148                 SongInfo song{ data.second };
149                 if (song.listen_time >= 20 && song.length() > 0)
150                 {
151                     song.file_path = data.first.path;
152                     m_data_list.push_back(SongInfoToListItem(song));
153                 }
154             }
155         });
156 
157     //先按累计收听时间从大到小排序
158     std::sort(m_data_list.begin(), m_data_list.end(), [](const ListItem& a, const ListItem& b)
159     {
160         return a.total_time > b.total_time;
161     });
162 
163     //再按累计次数从大到小排序,并确保累计次数相同的项目按累计时间从大到小排序
164     std::stable_sort(m_data_list.begin(), m_data_list.end(), [](const ListItem& a, const ListItem& b)
165     {
166         double times_a, times_b;
167         times_a = CCommon::DoubleRound(a.times, 1);
168         times_b = CCommon::DoubleRound(b.times, 1);
169         return times_a > times_b;
170     });
171 
172     //将数据显示到列表中
173 	ShowData();
174 
175 	return TRUE;  // return TRUE unless you set the focus to a control
176 				  // 异常: OCX 属性页应返回 FALSE
177 }
178 
179 
OnBnClickedExportButton()180 void CListenTimeStatisticsDlg::OnBnClickedExportButton()
181 {
182 	// TODO: 在此添加控件通知处理程序代码
183 
184 	//弹出保存对话框
185 
186     wstring filter = FilterHelper::GetListenTimeFilter();
187     wstring file_name = theApp.m_str_table.LoadText(L"TITLE_LISTEN_TIME");
188 	CString str_cur_date;
189 	SYSTEMTIME cur_time;
190 	GetLocalTime(&cur_time);
191 	str_cur_date.Format(_T("_%.4d-%.2d-%.2d"), cur_time.wYear, cur_time.wMonth, cur_time.wDay);
192 	file_name += str_cur_date;
193     CCommon::FileNameNormalize(file_name);
194 
195 	// 构造保存文件对话框
196     CFileDialog fileDlg(FALSE, _T("csv"), file_name.c_str(), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, filter.c_str(), this);
197 	// 显示保存文件对话框
198 	if (IDOK == fileDlg.DoModal())
199 	{
200         //生成导出的csv文本
201         std::wstringstream wss;
202         wss << theApp.m_str_table.LoadText(L"TXT_SERIAL_NUMBER") << L','
203             << theApp.m_str_table.LoadText(L"TXT_TRACK") << L','
204             << theApp.m_str_table.LoadText(L"TXT_PATH") << L','
205             << theApp.m_str_table.LoadText(L"TXT_LISTEN_TIME_TOTAL_TIME") << L','
206             << theApp.m_str_table.LoadText(L"TXT_LENGTH") << L','
207             << theApp.m_str_table.LoadText(L"TXT_LISTEN_TIME_TOTAL_COUNT") << L'\n';
208 
209         int list_size = m_list_ctrl.GetItemCount();
210         for (int i = 0; i < list_size; i++)
211         {
212             const int COLUMN = 6;
213             for (int j = 0; j < COLUMN; j++)
214             {
215                 CString item_text{ m_list_ctrl.GetItemText(i, j).GetString() };
216                 CCommon::StringCsvNormalize(item_text);
217                 wss << item_text.GetString();
218                 if (j == COLUMN - 1)
219                     wss << L'\n';
220                 else
221                     wss << L',';
222             }
223         }
224 
225         ofstream out_put{ fileDlg.GetPathName().GetString() };
226         out_put << CCommon::UnicodeToStr(wss.str(), CodeType::UTF8);
227         out_put.close();
228     }
229 }
230 
231 
OnBnClickedClearButton()232 void CListenTimeStatisticsDlg::OnBnClickedClearButton()
233 {
234     // TODO: 在此添加控件通知处理程序代码
235     const wstring& info = theApp.m_str_table.LoadText(L"MSG_LISTEN_TIME_CLEAR_WARNING");
236     if (MessageBox(info.c_str(), NULL, MB_ICONINFORMATION | MB_OKCANCEL) == IDOK)
237     {
238         CSongDataManager::GetInstance().ClearPlayTime();
239         m_list_ctrl.DeleteAllItems();
240     }
241 }
242 
243 
OnHdnItemclickList1(NMHDR * pNMHDR,LRESULT * pResult)244 void CListenTimeStatisticsDlg::OnHdnItemclickList1(NMHDR *pNMHDR, LRESULT *pResult)
245 {
246     LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
247     // TODO: 在此添加控件通知处理程序代码
248     if (phdr->hdr.hwndFrom == m_list_ctrl.GetHeaderCtrl()->GetSafeHwnd())
249     {
250         static bool ascending = true;
251         ascending = !ascending;
252 
253         static int last_item = -1;
254         if (last_item != phdr->iItem)
255         {
256             last_item = phdr->iItem;
257             ascending = false;
258         }
259 
260         //对列表排序
261         switch (phdr->iItem)
262         {
263         case COL_INDEX:
264             break;
265         case COL_TRACK:
266             std::sort(m_data_list.begin(), m_data_list.end(), [](const ListItem& a, const ListItem& b) { if (ascending) return CCommon::StringCompareInLocalLanguage(a.name, b.name) < 0; else return CCommon::StringCompareInLocalLanguage(a.name, b.name) > 0; });
267             ShowData(false);
268             break;
269         case COL_PATH:
270             std::sort(m_data_list.begin(), m_data_list.end(), [](const ListItem& a, const ListItem& b) { if (ascending) return CCommon::StringCompareInLocalLanguage(a.path, b.path) < 0; else return CCommon::StringCompareInLocalLanguage(a.path, b.path) > 0; });
271             ShowData(false);
272             break;
273         case COL_TOTAL_TIME:
274             std::sort(m_data_list.begin(), m_data_list.end(), [](const ListItem& a, const ListItem& b) { if (ascending) return a.total_time < b.total_time; else return a.total_time > b.total_time; });
275             ShowData(false);
276             break;
277         case COL_LENGTH:
278             std::sort(m_data_list.begin(), m_data_list.end(), [](const ListItem& a, const ListItem& b) { if (ascending) return a.length < b.length; else return a.length > b.length; });
279             ShowData(false);
280             break;
281         case COL_TIMES:
282             std::sort(m_data_list.begin(), m_data_list.end(), [](const ListItem& a, const ListItem& b) { if (ascending) return a.times < b.times; else return a.times > b.times; });
283             ShowData(false);
284             break;
285         default:
286             break;
287         }
288     }
289     *pResult = 0;
290 }
291