|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include <stdio.h> |
|
8 #include <windows.h> |
|
9 #include <commctrl.h> |
|
10 #include <process.h> |
|
11 #include <io.h> |
|
12 |
|
13 #include "resource.h" |
|
14 #include "progressui.h" |
|
15 #include "readstrings.h" |
|
16 #include "errors.h" |
|
17 |
|
18 #define TIMER_ID 1 |
|
19 #define TIMER_INTERVAL 100 |
|
20 |
|
21 #define RESIZE_WINDOW(hwnd, extrax, extray) \ |
|
22 { \ |
|
23 RECT windowSize; \ |
|
24 GetWindowRect(hwnd, &windowSize); \ |
|
25 SetWindowPos(hwnd, 0, 0, 0, windowSize.right - windowSize.left + extrax, \ |
|
26 windowSize.bottom - windowSize.top + extray, \ |
|
27 SWP_NOMOVE | SWP_NOZORDER); \ |
|
28 } |
|
29 |
|
30 #define MOVE_WINDOW(hwnd, dx, dy) \ |
|
31 { \ |
|
32 RECT rc; \ |
|
33 POINT pt; \ |
|
34 GetWindowRect(hwnd, &rc); \ |
|
35 pt.x = rc.left; \ |
|
36 pt.y = rc.top; \ |
|
37 ScreenToClient(GetParent(hwnd), &pt); \ |
|
38 SetWindowPos(hwnd, 0, pt.x + dx, pt.y + dy, 0, 0, \ |
|
39 SWP_NOSIZE | SWP_NOZORDER); \ |
|
40 } |
|
41 |
|
42 static float sProgress; // between 0 and 100 |
|
43 static BOOL sQuit = FALSE; |
|
44 static BOOL sIndeterminate = FALSE; |
|
45 static StringTable sUIStrings; |
|
46 |
|
47 static BOOL |
|
48 GetStringsFile(WCHAR filename[MAX_PATH]) |
|
49 { |
|
50 if (!GetModuleFileNameW(nullptr, filename, MAX_PATH)) |
|
51 return FALSE; |
|
52 |
|
53 WCHAR *dot = wcsrchr(filename, '.'); |
|
54 if (!dot || wcsicmp(dot + 1, L"exe")) |
|
55 return FALSE; |
|
56 |
|
57 wcscpy(dot + 1, L"ini"); |
|
58 return TRUE; |
|
59 } |
|
60 |
|
61 static void |
|
62 UpdateDialog(HWND hDlg) |
|
63 { |
|
64 int pos = int(sProgress + 0.5f); |
|
65 HWND hWndPro = GetDlgItem(hDlg, IDC_PROGRESS); |
|
66 SendMessage(hWndPro, PBM_SETPOS, pos, 0L); |
|
67 } |
|
68 |
|
69 // The code in this function is from MSDN: |
|
70 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/dialogboxes/usingdialogboxes.asp |
|
71 static void |
|
72 CenterDialog(HWND hDlg) |
|
73 { |
|
74 RECT rc, rcOwner, rcDlg; |
|
75 |
|
76 // Get the owner window and dialog box rectangles. |
|
77 HWND desktop = GetDesktopWindow(); |
|
78 |
|
79 GetWindowRect(desktop, &rcOwner); |
|
80 GetWindowRect(hDlg, &rcDlg); |
|
81 CopyRect(&rc, &rcOwner); |
|
82 |
|
83 // Offset the owner and dialog box rectangles so that |
|
84 // right and bottom values represent the width and |
|
85 // height, and then offset the owner again to discard |
|
86 // space taken up by the dialog box. |
|
87 |
|
88 OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); |
|
89 OffsetRect(&rc, -rc.left, -rc.top); |
|
90 OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); |
|
91 |
|
92 // The new position is the sum of half the remaining |
|
93 // space and the owner's original position. |
|
94 |
|
95 SetWindowPos(hDlg, |
|
96 HWND_TOP, |
|
97 rcOwner.left + (rc.right / 2), |
|
98 rcOwner.top + (rc.bottom / 2), |
|
99 0, 0, // ignores size arguments |
|
100 SWP_NOSIZE); |
|
101 } |
|
102 |
|
103 static void |
|
104 InitDialog(HWND hDlg) |
|
105 { |
|
106 WCHAR szwTitle[MAX_TEXT_LEN]; |
|
107 WCHAR szwInfo[MAX_TEXT_LEN]; |
|
108 |
|
109 MultiByteToWideChar(CP_UTF8, 0, sUIStrings.title, -1, szwTitle, |
|
110 sizeof(szwTitle)/sizeof(szwTitle[0])); |
|
111 MultiByteToWideChar(CP_UTF8, 0, sUIStrings.info, -1, szwInfo, |
|
112 sizeof(szwInfo)/sizeof(szwInfo[0])); |
|
113 |
|
114 SetWindowTextW(hDlg, szwTitle); |
|
115 SetWindowTextW(GetDlgItem(hDlg, IDC_INFO), szwInfo); |
|
116 |
|
117 // Set dialog icon |
|
118 HICON hIcon = LoadIcon(GetModuleHandle(nullptr), |
|
119 MAKEINTRESOURCE(IDI_DIALOG)); |
|
120 if (hIcon) |
|
121 SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon); |
|
122 |
|
123 HWND hWndPro = GetDlgItem(hDlg, IDC_PROGRESS); |
|
124 SendMessage(hWndPro, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); |
|
125 if (sIndeterminate) { |
|
126 LONG_PTR val = GetWindowLongPtr(hWndPro, GWL_STYLE); |
|
127 SetWindowLongPtr(hWndPro, GWL_STYLE, val|PBS_MARQUEE); |
|
128 SendMessage(hWndPro,(UINT) PBM_SETMARQUEE,(WPARAM) TRUE,(LPARAM)50 ); |
|
129 } |
|
130 |
|
131 // Resize the dialog to fit all of the text if necessary. |
|
132 RECT infoSize, textSize; |
|
133 HWND hWndInfo = GetDlgItem(hDlg, IDC_INFO); |
|
134 |
|
135 // Get the control's font for calculating the new size for the control |
|
136 HDC hDCInfo = GetDC(hWndInfo); |
|
137 HFONT hInfoFont, hOldFont; |
|
138 hInfoFont = (HFONT)SendMessage(hWndInfo, WM_GETFONT, 0, 0); |
|
139 |
|
140 if (hInfoFont) |
|
141 hOldFont = (HFONT)SelectObject(hDCInfo, hInfoFont); |
|
142 |
|
143 // Measure the space needed for the text on a single line. DT_CALCRECT means |
|
144 // nothing is drawn. |
|
145 if (DrawText(hDCInfo, szwInfo, -1, &textSize, |
|
146 DT_CALCRECT | DT_NOCLIP | DT_SINGLELINE)) { |
|
147 GetClientRect(hWndInfo, &infoSize); |
|
148 SIZE extra; |
|
149 // Calculate the additional space needed for the text by subtracting from |
|
150 // the rectangle returned by DrawText the existing client rectangle's width |
|
151 // and height. |
|
152 extra.cx = (textSize.right - textSize.left) - \ |
|
153 (infoSize.right - infoSize.left); |
|
154 extra.cy = (textSize.bottom - textSize.top) - \ |
|
155 (infoSize.bottom - infoSize.top); |
|
156 if (extra.cx < 0) |
|
157 extra.cx = 0; |
|
158 if (extra.cy < 0) |
|
159 extra.cy = 0; |
|
160 if ((extra.cx > 0) || (extra.cy > 0)) { |
|
161 RESIZE_WINDOW(hDlg, extra.cx, extra.cy); |
|
162 RESIZE_WINDOW(hWndInfo, extra.cx, extra.cy); |
|
163 RESIZE_WINDOW(hWndPro, extra.cx, 0); |
|
164 MOVE_WINDOW(hWndPro, 0, extra.cy); |
|
165 } |
|
166 } |
|
167 |
|
168 if (hOldFont) |
|
169 SelectObject(hDCInfo, hOldFont); |
|
170 |
|
171 ReleaseDC(hWndInfo, hDCInfo); |
|
172 |
|
173 CenterDialog(hDlg); // make dialog appear in the center of the screen |
|
174 |
|
175 SetTimer(hDlg, TIMER_ID, TIMER_INTERVAL, nullptr); |
|
176 } |
|
177 |
|
178 // Message handler for update dialog. |
|
179 static LRESULT CALLBACK |
|
180 DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) |
|
181 { |
|
182 switch (message) |
|
183 { |
|
184 case WM_INITDIALOG: |
|
185 InitDialog(hDlg); |
|
186 return TRUE; |
|
187 |
|
188 case WM_TIMER: |
|
189 if (sQuit) { |
|
190 EndDialog(hDlg, 0); |
|
191 } else { |
|
192 UpdateDialog(hDlg); |
|
193 } |
|
194 return TRUE; |
|
195 |
|
196 case WM_COMMAND: |
|
197 return TRUE; |
|
198 } |
|
199 return FALSE; |
|
200 } |
|
201 |
|
202 int |
|
203 InitProgressUI(int *argc, NS_tchar ***argv) |
|
204 { |
|
205 return 0; |
|
206 } |
|
207 |
|
208 /** |
|
209 * Initializes the progress UI strings |
|
210 * |
|
211 * @return 0 on success, -1 on error |
|
212 */ |
|
213 int |
|
214 InitProgressUIStrings() { |
|
215 // If we do not have updater.ini, then we should not bother showing UI. |
|
216 WCHAR filename[MAX_PATH]; |
|
217 if (!GetStringsFile(filename)) { |
|
218 return -1; |
|
219 } |
|
220 |
|
221 if (_waccess(filename, 04)) { |
|
222 return -1; |
|
223 } |
|
224 |
|
225 // If the updater.ini doesn't have the required strings, then we should not |
|
226 // bother showing UI. |
|
227 if (ReadStrings(filename, &sUIStrings) != OK) { |
|
228 return -1; |
|
229 } |
|
230 |
|
231 return 0; |
|
232 } |
|
233 |
|
234 int |
|
235 ShowProgressUI(bool indeterminate, bool initUIStrings) |
|
236 { |
|
237 sIndeterminate = indeterminate; |
|
238 if (!indeterminate) { |
|
239 // Only show the Progress UI if the process is taking a significant amount of |
|
240 // time where a significant amount of time is defined as .5 seconds after |
|
241 // ShowProgressUI is called sProgress is less than 70. |
|
242 Sleep(500); |
|
243 |
|
244 if (sQuit || sProgress > 70.0f) |
|
245 return 0; |
|
246 } |
|
247 |
|
248 if (initUIStrings && InitProgressUIStrings() == -1) { |
|
249 return -1; |
|
250 } |
|
251 |
|
252 INITCOMMONCONTROLSEX icc = { |
|
253 sizeof(INITCOMMONCONTROLSEX), |
|
254 ICC_PROGRESS_CLASS |
|
255 }; |
|
256 InitCommonControlsEx(&icc); |
|
257 |
|
258 DialogBox(GetModuleHandle(nullptr), |
|
259 MAKEINTRESOURCE(IDD_DIALOG), nullptr, |
|
260 (DLGPROC) DialogProc); |
|
261 |
|
262 return 0; |
|
263 } |
|
264 |
|
265 void |
|
266 QuitProgressUI() |
|
267 { |
|
268 sQuit = TRUE; |
|
269 } |
|
270 |
|
271 void |
|
272 UpdateProgressUI(float progress) |
|
273 { |
|
274 sProgress = progress; // 32-bit writes are atomic |
|
275 } |