1 /*
2 * PROJECT: ReactOS system libraries
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Classes for displaying progress dialog.
5 * COPYRIGHT: Copyright 2021 He Yang <1160386205@qq.com>
6 */
7
8 #include "iernonce.h"
9 #include <process.h>
10
11 #define ITEM_VPADDING 3
12 #define ITEM_LEFTPADDING 22
13
CreateBoldFont(_In_ HFONT hOrigFont)14 HFONT CreateBoldFont(_In_ HFONT hOrigFont)
15 {
16 LOGFONTW fontAttributes = { 0 };
17 GetObjectW(hOrigFont, sizeof(fontAttributes), &fontAttributes);
18 fontAttributes.lfWeight = FW_BOLD;
19
20 return CreateFontIndirectW(&fontAttributes);
21 }
22
ProgressDlg(_In_ RunOnceExInstance & RunOnceExInst)23 ProgressDlg::ProgressDlg(_In_ RunOnceExInstance &RunOnceExInst) :
24 m_hListBox(NULL),
25 m_hBoldFont(NULL),
26 m_PointedItem(0),
27 m_RunOnceExInst(RunOnceExInst)
28 { ; }
29
RunDialogBox()30 BOOL ProgressDlg::RunDialogBox()
31 {
32 // Show the dialog and run the items only when the list is not empty.
33 if (m_RunOnceExInst.m_SectionList.GetSize() != 0)
34 {
35 return (DoModal() == 1);
36 }
37 return TRUE;
38 }
39
CalcTextRect(_In_ LPCWSTR lpText,_Inout_ PRECT pRect)40 void ProgressDlg::CalcTextRect(
41 _In_ LPCWSTR lpText,
42 _Inout_ PRECT pRect)
43 {
44 HDC hdc = ::GetDC(m_hListBox);
45 ::GetClientRect(m_hListBox, pRect);
46
47 pRect->bottom = pRect->top;
48 pRect->left += ITEM_LEFTPADDING;
49
50 HFONT OldFont = SelectFont(hdc, GetFont());
51 DrawTextW(hdc, lpText, -1, pRect, DT_CALCRECT | DT_WORDBREAK);
52 SelectFont(hdc, OldFont);
53 ::ReleaseDC(m_hListBox, hdc);
54
55 pRect->bottom -= pRect->top;
56 pRect->bottom += ITEM_VPADDING * 2;
57 pRect->top = 0;
58 pRect->right -= pRect->left;
59 pRect->left = 0;
60 }
61
ResizeListBoxAndDialog(_In_ int NewHeight)62 void ProgressDlg::ResizeListBoxAndDialog(_In_ int NewHeight)
63 {
64 RECT ListBoxRect;
65 RECT DlgRect;
66 ::GetWindowRect(m_hListBox, &ListBoxRect);
67 GetWindowRect(&DlgRect);
68
69 int HeightDiff = NewHeight - (ListBoxRect.bottom - ListBoxRect.top);
70
71 ::SetWindowPos(m_hListBox, NULL, 0, 0,
72 ListBoxRect.right - ListBoxRect.left, NewHeight,
73 SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
74
75 SetWindowPos(HWND_TOP, 0, 0,
76 DlgRect.right - DlgRect.left,
77 DlgRect.bottom - DlgRect.top + HeightDiff,
78 SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
79 }
80
81 unsigned int __stdcall
RunOnceExExecThread(_In_ void * Param)82 RunOnceExExecThread(_In_ void *Param)
83 {
84 ProgressDlg *pProgressDlg = (ProgressDlg *)Param;
85
86 pProgressDlg->m_RunOnceExInst.Exec(pProgressDlg->m_hWnd);
87 return 0;
88 }
89
90 BOOL
ProcessWindowMessage(_In_ HWND hwnd,_In_ UINT message,_In_ WPARAM wParam,_In_ LPARAM lParam,_Out_ LRESULT & lResult,_In_ DWORD dwMsgMapID)91 ProgressDlg::ProcessWindowMessage(
92 _In_ HWND hwnd,
93 _In_ UINT message,
94 _In_ WPARAM wParam,
95 _In_ LPARAM lParam,
96 _Out_ LRESULT& lResult,
97 _In_ DWORD dwMsgMapID)
98 {
99 lResult = 0;
100 switch (message)
101 {
102 case WM_INITDIALOG:
103 {
104 if (!m_RunOnceExInst.m_Title.IsEmpty())
105 {
106 SetWindowTextW(m_RunOnceExInst.m_Title);
107 }
108
109 m_hListBox = GetDlgItem(IDC_LB_ITEMS);
110
111 m_hBoldFont = CreateBoldFont(GetFont());
112
113 m_hArrowBmp = LoadBitmapW(NULL, MAKEINTRESOURCE(OBM_MNARROW));
114 GetObjectW(m_hArrowBmp, sizeof(BITMAP), &m_ArrowBmp);
115
116 // Add all sections with non-empty title into listbox
117 int TotalHeight = 0;
118 for (int i = 0; i < m_RunOnceExInst.m_SectionList.GetSize(); i++)
119 {
120 RunOnceExSection &Section = m_RunOnceExInst.m_SectionList[i];
121
122 if (!Section.m_SectionTitle.IsEmpty())
123 {
124 INT Index = ListBox_AddString(m_hListBox, Section.m_SectionTitle);
125 TotalHeight += ListBox_GetItemHeight(m_hListBox, Index);
126 ListBox_SetItemData(m_hListBox, Index, i);
127 }
128 }
129
130 // Remove the sunken-edged border from the listbox.
131 ::SetWindowLongPtr(m_hListBox, GWL_EXSTYLE, ::GetWindowLongPtr(m_hListBox, GWL_EXSTYLE) & ~WS_EX_CLIENTEDGE);
132
133 ResizeListBoxAndDialog(TotalHeight);
134
135 // Launch a thread to execute tasks.
136 HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, RunOnceExExecThread, (void *)this, 0, NULL);
137 if (hThread == INVALID_HANDLE_VALUE)
138 {
139 EndDialog(0);
140 return TRUE;
141 }
142 CloseHandle(hThread);
143
144 lResult = TRUE; // set keyboard focus to the dialog box control.
145 break;
146 }
147
148 case WM_MEASUREITEM:
149 {
150 PMEASUREITEMSTRUCT pMeasureItem = (PMEASUREITEMSTRUCT)lParam;
151 RECT TextRect = { 0 };
152
153 CStringW ItemText;
154 ListBox_GetText(m_hListBox, pMeasureItem->itemID,
155 ItemText.GetBuffer(ListBox_GetTextLen(m_hListBox,
156 pMeasureItem->itemID) + 1));
157
158 CalcTextRect(ItemText, &TextRect);
159
160 ItemText.ReleaseBuffer();
161
162 pMeasureItem->itemHeight = TextRect.bottom - TextRect.top;
163 pMeasureItem->itemWidth = TextRect.right - TextRect.left;
164
165 break;
166 }
167
168 case WM_DRAWITEM:
169 {
170 LPDRAWITEMSTRUCT pDrawItem = (PDRAWITEMSTRUCT)lParam;
171 CStringW ItemText;
172
173 ListBox_GetText(m_hListBox, pDrawItem->itemID,
174 ItemText.GetBuffer(ListBox_GetTextLen(m_hListBox,
175 pDrawItem->itemID) + 1));
176
177 SetBkMode(pDrawItem->hDC, TRANSPARENT);
178
179 HFONT hOldFont = NULL;
180 if (m_PointedItem == (INT)pDrawItem->itemData)
181 {
182 HDC hCompDC = CreateCompatibleDC(pDrawItem->hDC);
183
184 SelectBitmap(hCompDC, m_hArrowBmp);
185
186 int IconLeftPadding = (ITEM_LEFTPADDING - m_ArrowBmp.bmWidth) / 2;
187 int IconTopPadding = (pDrawItem->rcItem.bottom - pDrawItem->rcItem.top - m_ArrowBmp.bmHeight) / 2;
188
189 BitBlt(pDrawItem->hDC, IconLeftPadding, pDrawItem->rcItem.top + IconTopPadding,
190 m_ArrowBmp.bmWidth, m_ArrowBmp.bmHeight, hCompDC, 0, 0, SRCAND);
191
192 DeleteDC(hCompDC);
193
194 hOldFont = SelectFont(pDrawItem->hDC, m_hBoldFont);
195 }
196
197 pDrawItem->rcItem.left += ITEM_LEFTPADDING;
198 pDrawItem->rcItem.top += ITEM_VPADDING;
199 DrawTextW(pDrawItem->hDC, ItemText, -1,
200 &(pDrawItem->rcItem), DT_WORDBREAK);
201
202 if (hOldFont)
203 {
204 SelectFont(pDrawItem->hDC, hOldFont);
205 }
206 ItemText.ReleaseBuffer();
207
208 break;
209 }
210
211 case WM_SETINDEX:
212 {
213 if ((int)wParam == m_RunOnceExInst.m_SectionList.GetSize())
214 {
215 // All sections are handled, lParam is bSuccess.
216 EndDialog(lParam);
217 }
218 m_PointedItem = wParam;
219 InvalidateRect(NULL);
220 break;
221 }
222
223 case WM_CTLCOLORLISTBOX:
224 {
225 lResult = (LRESULT)GetStockBrush(NULL_BRUSH);
226 break;
227 }
228
229 case WM_DESTROY:
230 {
231 DeleteObject(m_hArrowBmp);
232 DeleteFont(m_hBoldFont);
233 break;
234 }
235 }
236 return TRUE;
237 }
238