1 // BaseDialog.cpp : 实现文件
2 //
3
4 #include "stdafx.h"
5 #include "BaseDialog.h"
6 #include "IniHelper.h"
7 #include "MusicPlayer2.h"
8
9 // CBaseDialog 对话框
10 std::map<CString, HWND> CBaseDialog::m_unique_hwnd;
11
IMPLEMENT_DYNAMIC(CBaseDialog,CDialog)12 IMPLEMENT_DYNAMIC(CBaseDialog, CDialog)
13
14 CBaseDialog::CBaseDialog(UINT nIDTemplate, CWnd* pParent /*=NULL*/)
15 : CDialog(nIDTemplate, pParent)
16 {
17 m_nDialogID = nIDTemplate;
18 }
19
~CBaseDialog()20 CBaseDialog::~CBaseDialog()
21 {
22 }
23
SetBackgroundColor(COLORREF color,BOOL bRepaint)24 void CBaseDialog::SetBackgroundColor(COLORREF color, BOOL bRepaint)
25 {
26 if (m_brBkgr.GetSafeHandle() != NULL)
27 {
28 m_brBkgr.DeleteObject();
29 }
30
31 if (color != (COLORREF)-1)
32 {
33 m_brBkgr.CreateSolidBrush(color);
34 }
35
36 if (bRepaint && GetSafeHwnd() != NULL)
37 {
38 Invalidate();
39 UpdateWindow();
40 }
41 }
42
GetUniqueHandel(LPCTSTR dlg_name)43 HWND CBaseDialog::GetUniqueHandel(LPCTSTR dlg_name)
44 {
45 return m_unique_hwnd[dlg_name];
46 }
47
AllUniqueHandels()48 const std::map<CString, HWND>& CBaseDialog::AllUniqueHandels()
49 {
50 return m_unique_hwnd;
51 }
52
CloseAllWindow()53 void CBaseDialog::CloseAllWindow()
54 {
55 //确保在退出前关闭所有窗口
56 for (const auto& item : AllUniqueHandels())
57 {
58 ::SendMessage(item.second, WM_COMMAND, IDCANCEL, 0);
59 }
60 }
61
ShowModelessDialog(UINT id)62 void CBaseDialog::ShowModelessDialog(UINT id)
63 {
64 m_is_modeless_dialog = true;
65 if (!IsWindow(m_hWnd))
66 {
67 Create(id);
68 ShowWindow(SW_SHOW);
69 }
70 else
71 {
72 ShowWindow(SW_SHOWNORMAL);
73 }
74 }
75
LoadConfig()76 void CBaseDialog::LoadConfig()
77 {
78 ASSERT(!GetDialogName().IsEmpty());
79 ASSERT(IsRememberDialogSizeEnable());
80 CIniHelper ini{ theApp.m_config_path };
81 //载入窗口大小设置
82 m_window_size.cx = ini.GetInt(_T("window_size"), GetDialogName() + _T("_width"), -1);
83 m_window_size.cy = ini.GetInt(_T("window_size"), GetDialogName() + _T("_height"), -1);
84 }
85
SaveConfig() const86 void CBaseDialog::SaveConfig() const
87 {
88 ASSERT(!GetDialogName().IsEmpty());
89 ASSERT(IsRememberDialogSizeEnable());
90 CIniHelper ini{ theApp.m_config_path };
91 //保存窗口大小设置
92 ini.WriteInt(_T("window_size"), GetDialogName() + _T("_width"), m_window_size.cx);
93 ini.WriteInt(_T("window_size"), GetDialogName() + _T("_height"), m_window_size.cy);
94 ini.Save();
95 }
96
ReLoadLayoutResource()97 void CBaseDialog::ReLoadLayoutResource()
98 {
99 ASSERT(m_nDialogID);
100 CMFCDynamicLayout* pDynamicLayout = GetDynamicLayout();
101 if (pDynamicLayout)
102 {
103 HRSRC layoutRes = ::FindResourceW(NULL, MAKEINTRESOURCEW(m_nDialogID), RT_DIALOG_LAYOUT);
104 if (layoutRes)
105 {
106 HGLOBAL hResData = ::LoadResource(NULL, layoutRes);
107 if (hResData)
108 {
109 LPVOID layoutResData = ::LockResource(hResData);
110 DWORD layoutResSize = ::SizeofResource(NULL, layoutRes);
111 // std::wstring data(static_cast<wchar_t*>(layoutResData), layoutResSize / sizeof(wchar_t));
112 pDynamicLayout->LoadResource(this, layoutResData, layoutResSize);
113 }
114 }
115 }
116 }
117
SetIcon(IconMgr::IconType type,BOOL bBigIcon)118 void CBaseDialog::SetIcon(IconMgr::IconType type, BOOL bBigIcon)
119 {
120 if (bBigIcon)
121 CDialog::SetIcon(theApp.m_icon_mgr.GetHICON(type, IconMgr::IconStyle::IS_OutlinedDark, IconMgr::IconSize::IS_ALL), TRUE);
122 else
123 CDialog::SetIcon(theApp.m_icon_mgr.GetHICON(type, IconMgr::IconStyle::IS_OutlinedDark, IconMgr::IconSize::IS_DPI_16), FALSE);
124 }
125
SetButtonIcon(UINT id,IconMgr::IconType type)126 void CBaseDialog::SetButtonIcon(UINT id, IconMgr::IconType type)
127 {
128 CWnd* dlgItem = GetDlgItem(id);
129 CButton* close_btn = static_cast<CButton*>(dlgItem);
130 if (close_btn != nullptr)
131 close_btn->SetIcon(theApp.m_icon_mgr.GetHICON(type, IconMgr::IconStyle::IS_OutlinedDark, IconMgr::IconSize::IS_DPI_16));
132 }
133
ShowDlgCtrl(UINT id,bool show)134 void CBaseDialog::ShowDlgCtrl(UINT id, bool show)
135 {
136 CWnd* pCtrl = GetDlgItem(id);
137 if (pCtrl != nullptr)
138 pCtrl->ShowWindow(show);
139 }
140
EnableDlgCtrl(UINT id,bool enable)141 void CBaseDialog::EnableDlgCtrl(UINT id, bool enable)
142 {
143 CWnd* pCtrl = GetDlgItem(id);
144 if (pCtrl != nullptr)
145 pCtrl->EnableWindow(enable);
146 }
147
GetTextExtent(const CString & text)148 CRect CBaseDialog::GetTextExtent(const CString& text)
149 {
150 ASSERT(m_pDC != nullptr); // m_pDC由OnInitDialog负责申请释放
151 if (m_pDC == nullptr)
152 return CRect();
153 if (text.IsEmpty())
154 return CRect();
155 CRect text_size;
156 m_pDC->DrawTextW(text, &text_size, DT_CALCRECT); // 使用CDC::DrawTextW测量文本宽度(CDC::GetTextExtent是理论宽度,不准确)
157 return text_size;
158 }
159
RepositionTextBasedControls(const vector<CtrlTextInfo> & items,CtrlTextInfo::Width center_min_width)160 void CBaseDialog::RepositionTextBasedControls(const vector<CtrlTextInfo>& items, CtrlTextInfo::Width center_min_width)
161 {
162 ASSERT(m_pDC != nullptr); // 此方法仅在InitializeControls期间可用
163 if (m_pDC == nullptr)
164 return;
165 int center_width = theApp.DPI(center_min_width);
166 std::map<int, pair<int, int>> col_info;
167 struct itemInfo
168 {
169 int col_index;
170 CWnd* p;
171 CRect rect;
172 };
173 vector<itemInfo> items_info;
174 int center_left{ 0 }, center_right{ INT_MAX };
175
176 for (const auto& item : items)
177 {
178 ASSERT(item.col_index != CtrlTextInfo::UN_USE);
179 ASSERT(item.id != 0);
180 // 获取所有列所需dx,以及左贴靠元素的右边缘center_left,右贴靠元素的左边缘center_right
181 CWnd* pItem = GetDlgItem(item.id);
182 if (pItem == nullptr)
183 continue;
184 CRect rect{};
185 pItem->GetWindowRect(&rect);
186 ScreenToClient(&rect);
187 CString text;
188 pItem->GetWindowTextW(text);
189 int dx = GetTextExtent(text).Width() + theApp.DPI(item.ext_width) - rect.Width();
190 if (dx < 0) dx = 0; // 文字只增加控件宽度
191 if (col_info[item.col_index].first < dx) // 取此列元素中宽度增长最多的
192 col_info[item.col_index].first = dx;
193 if (item.col_index < 0 && center_left < rect.right)
194 center_left = rect.right;
195 if (item.col_index > 0 && center_right > rect.left)
196 center_right = rect.left;
197 if (item.col_index == 0) // col_index为0的控件可能有多个
198 {
199 if(center_left < rect.left)
200 center_left = rect.left;
201 if(center_right > rect.right)
202 center_right = rect.right;
203 }
204 items_info.emplace_back(itemInfo{ item.col_index, pItem, std::move(rect) });
205 }
206
207 if (center_right == INT_MAX) // 如果控件全部都是左贴靠的那么以窗口右边缘作为剩余空间的右边缘
208 {
209 CRect dlg_rect{};
210 GetClientRect(&dlg_rect);
211 center_right = dlg_rect.Width();
212 }
213 // 此断言触发说明资源文件中的原始布局没有给中间控件/空闲空间留够宽度
214 ASSERT(center_right - center_left >= center_width);
215 int dx_sum_left{}, dx_sum_right{};
216 // 因为同一col_index可以有多个控件&没有要求顺序所以控件的最终位置必须可以无状态的计算出来
217 for (auto& a : col_info)
218 {
219 if (a.first < 0)
220 {
221 a.second.second = dx_sum_left; // 存储此列之前控件的总dx,即此列控件的右移距离
222 dx_sum_left += a.second.first;
223 }
224 else if (a.first > 0)
225 {
226 a.second.second = dx_sum_right;
227 dx_sum_right += a.second.first;
228 }
229 }
230 float scale{ 1.0f };
231 if (center_right - center_left - dx_sum_left - dx_sum_right < center_width)
232 {
233 // ASSERT(false);
234 // 现在加载的文本使此行的中间控件/空闲空间被挤压的太小
235 // 这需要重新设计窗口控件排布以适应当前翻译长度,这里先简单的把缺少的空间分摊给各dx
236 scale = static_cast<float>(center_right - center_left - center_width) / (dx_sum_left + dx_sum_right);
237 dx_sum_left = static_cast<int>(dx_sum_left * scale + 0.5f);
238 dx_sum_right = static_cast<int>(dx_sum_right * scale + 0.5f);
239 }
240 for (const auto& item : items_info)
241 {
242 const auto& rect = item.rect;
243 int dx = static_cast<int>(col_info[item.col_index].first * scale + 0.5f);
244 int sum_dx = static_cast<int>(col_info[item.col_index].second * scale + 0.5f);
245 if (item.col_index < 0)
246 item.p->SetWindowPos(nullptr, rect.left + sum_dx, rect.top, rect.Width() + dx, rect.Height(), SWP_NOZORDER);
247 else if (item.col_index > 0)
248 item.p->SetWindowPos(nullptr, rect.left + sum_dx - dx_sum_right, rect.top, rect.Width() + dx, rect.Height(), SWP_NOZORDER);
249 else if (item.col_index == 0)
250 item.p->SetWindowPos(nullptr, rect.left + dx_sum_left, rect.top, rect.Width() - dx_sum_left - dx_sum_right, rect.Height(), SWP_NOZORDER);
251 }
252 }
253
SetMinSize(int cx,int cy)254 void CBaseDialog::SetMinSize(int cx, int cy)
255 {
256 m_min_size.cx = cx;
257 m_min_size.cy = cy;
258 }
259
SetDlgControlText(int id,const wchar_t * key)260 void CBaseDialog::SetDlgControlText(int id, const wchar_t* key)
261 {
262 const wstring& temp = theApp.m_str_table.LoadText(key);
263 SetDlgItemTextW(id, temp.c_str());
264 }
265
DoDataExchange(CDataExchange * pDX)266 void CBaseDialog::DoDataExchange(CDataExchange* pDX)
267 {
268 CDialog::DoDataExchange(pDX);
269 }
270
271
BEGIN_MESSAGE_MAP(CBaseDialog,CDialog)272 BEGIN_MESSAGE_MAP(CBaseDialog, CDialog)
273 ON_WM_DESTROY()
274 ON_WM_GETMINMAXINFO()
275 ON_WM_SIZE()
276 ON_WM_ERASEBKGND()
277 ON_WM_CTLCOLOR()
278 ON_WM_CLOSE()
279 END_MESSAGE_MAP()
280
281
282 // CBaseDialog 消息处理程序
283
284
285 BOOL CBaseDialog::OnInitDialog()
286 {
287 if (!GetDialogName().IsEmpty())
288 {
289 m_unique_hwnd[GetDialogName()] = m_hWnd;
290 if (IsRememberDialogSizeEnable())
291 CBaseDialog::LoadConfig(); // 载入设置
292 }
293
294 CDialog::OnInitDialog();
295
296 // TODO: 在此添加额外的初始化
297
298 //获取初始时窗口的大小
299 if (m_min_size.cx <= 0 || m_min_size.cy <= 0)
300 {
301 CRect rect;
302 GetWindowRect(rect);
303 m_min_size.cx = rect.Width();
304 m_min_size.cy = rect.Height();
305 }
306
307 // 通用窗口/控件初始化操作
308
309 // 设置窗口字体
310 CCommon::SetDialogFont(this, &theApp.m_font_set.dlg.GetFont());
311 //为按钮添加图标
312 SetButtonIcon(IDCANCEL, IconMgr::IconType::IT_Cancel);
313 SetButtonIcon(IDOK, IconMgr::IconType::IT_Ok);
314 // 设置按钮文本
315 wstring temp;
316 temp = theApp.m_str_table.LoadText(L"TXT_OK");
317 SetDlgItemTextW(IDOK, temp.c_str());
318 temp = theApp.m_str_table.LoadText(L"TXT_CANCEL");
319 SetDlgItemTextW(IDCANCEL, temp.c_str());
320
321 // 在还原窗口大小之前(当前窗口状态与资源一致),派生类执行控件文本初始化及调整控件排布
322 // 与实际窗口大小相关的初始化(比如表格列宽)应在派生类的OnInitDialog进行
323 m_pDC = GetDC();
324 m_pDC->SelectObject(&theApp.m_font_set.dlg.GetFont());
325 bool rtn = InitializeControls();
326 ReleaseDC(m_pDC);
327 m_pDC = nullptr;
328 // 如果更改了控件排布那么应当返回true以向布局管理器应用控件调整(重新加载动态布局设置)
329 if (rtn)
330 ReLoadLayoutResource();
331
332 //初始化窗口大小
333 if (m_window_size.cx > 0 && m_window_size.cy > 0)
334 {
335 SetWindowPos(nullptr, 0, 0, m_window_size.cx, m_window_size.cy, SWP_NOZORDER | SWP_NOMOVE);
336 }
337
338 return TRUE; // return TRUE unless you set the focus to a control
339 // 异常: OCX 属性页应返回 FALSE
340 }
341
342
OnDestroy()343 void CBaseDialog::OnDestroy()
344 {
345 CDialog::OnDestroy();
346
347 // TODO: 在此处添加消息处理程序代码
348 if (!GetDialogName().IsEmpty())
349 {
350 m_unique_hwnd[GetDialogName()] = NULL;
351 if (IsRememberDialogSizeEnable())
352 CBaseDialog::SaveConfig();
353 }
354 }
355
356
OnGetMinMaxInfo(MINMAXINFO * lpMMI)357 void CBaseDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
358 {
359 // TODO: 在此添加消息处理程序代码和/或调用默认值
360 //限制窗口最小大小
361 lpMMI->ptMinTrackSize.x = m_min_size.cx; //设置最小宽度
362 lpMMI->ptMinTrackSize.y = m_min_size.cy; //设置最小高度
363
364 CDialog::OnGetMinMaxInfo(lpMMI);
365 }
366
367
OnSize(UINT nType,int cx,int cy)368 void CBaseDialog::OnSize(UINT nType, int cx, int cy)
369 {
370 CDialog::OnSize(nType, cx, cy);
371
372 // TODO: 在此处添加消息处理程序代码
373 if (nType != SIZE_MAXIMIZED && nType != SIZE_MINIMIZED)
374 {
375 CRect rect;
376 GetWindowRect(&rect);
377 m_window_size.cx = rect.Width();
378 m_window_size.cy = rect.Height();
379 }
380
381 }
382
383
DoModal()384 INT_PTR CBaseDialog::DoModal()
385 {
386 if (!GetDialogName().IsEmpty())
387 {
388 HWND unique_hwnd{ m_unique_hwnd[GetDialogName()] };
389 if (unique_hwnd != NULL) ///如果对话框已存在,则显示已存在的对话框
390 {
391 ::ShowWindow(unique_hwnd, SW_RESTORE);
392 ::SetForegroundWindow(unique_hwnd);
393 return 0;
394 }
395 }
396 return CDialog::DoModal();
397 }
398
399
OnEraseBkgnd(CDC * pDC)400 BOOL CBaseDialog::OnEraseBkgnd(CDC* pDC)
401 {
402 // 修改窗口背景(CDialogEx)
403 if (m_brBkgr.GetSafeHandle() != NULL)
404 {
405 ASSERT_VALID(pDC);
406 CRect rectClient;
407 GetClientRect(rectClient);
408 pDC->FillRect(rectClient, &m_brBkgr);
409 return TRUE;
410 }
411 return CDialog::OnEraseBkgnd(pDC);
412 }
413
414
OnCtlColor(CDC * pDC,CWnd * pWnd,UINT nCtlColor)415 HBRUSH CBaseDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
416 {
417 // 修改窗口背景(CDialogEx)
418 if (m_brBkgr.GetSafeHandle() != NULL)
419 {
420 #define AFX_MAX_CLASS_NAME 255
421 #define AFX_STATIC_CLASS _T("Static")
422 #define AFX_BUTTON_CLASS _T("Button")
423 // 上面两个是CDialogEx原有的(因为这两种比较原生,类名不在rc文件中直接出现?),下面是我自己加的
424 #define AFX_SLIDER_CLASS _T("msctls_trackbar32") // 滑动条控件CSliderCtrl及其派生类
425 #define AFX_SYSLINK_CLASS _T("SysLink") // 超链接控件CSysLink及其派生类
426
427 if (nCtlColor == CTLCOLOR_STATIC)
428 {
429 TCHAR lpszClassName[AFX_MAX_CLASS_NAME + 1];
430
431 ::GetClassName(pWnd->GetSafeHwnd(), lpszClassName, AFX_MAX_CLASS_NAME);
432 CString strClass = lpszClassName;
433
434 if (strClass == AFX_BUTTON_CLASS || strClass == AFX_STATIC_CLASS || strClass == AFX_SLIDER_CLASS || strClass == AFX_SYSLINK_CLASS)
435 {
436 pDC->SetBkMode(TRANSPARENT);
437
438 if (m_brBkgr.GetSafeHandle() != NULL && IsAppThemed())
439 {
440 return (HBRUSH)m_brBkgr.GetSafeHandle();
441 }
442 else
443 {
444 return (HBRUSH)::GetStockObject(HOLLOW_BRUSH);
445 }
446 }
447 }
448 }
449
450 return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
451 }
452
453
OnClose()454 void CBaseDialog::OnClose()
455 {
456 CDialog::OnClose();
457
458 if (m_is_modeless_dialog)
459 DestroyWindow();
460 }
461
462
OnOK()463 void CBaseDialog::OnOK()
464 {
465 CDialog::OnOK();
466 if (m_is_modeless_dialog)
467 DestroyWindow();
468 }
469
470
OnCancel()471 void CBaseDialog::OnCancel()
472 {
473 CDialog::OnCancel();
474 if (m_is_modeless_dialog)
475 DestroyWindow();
476 }
477