"Fossies" - the Fresh Open Source Software Archive 
Member "rufus-3.13/src/ui.c" (20 Nov 2020, 60036 Bytes) of package /linux/misc/rufus-3.13.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "ui.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3.12_vs_3.13.
1 /*
2 * Rufus: The Reliable USB Formatting Utility
3 * UI-related function calls
4 * Copyright © 2018-2020 Pete Batard <pete@akeo.ie>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
21 #ifdef _CRTDBG_MAP_ALLOC
22 #include <stdlib.h>
23 #include <crtdbg.h>
24 #endif
25
26 #include <windows.h>
27 #include <windowsx.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <oleacc.h>
32 #include <winioctl.h>
33 #include <assert.h>
34
35 #include "rufus.h"
36 #include "drive.h"
37 #include "missing.h"
38 #include "resource.h"
39 #include "msapi_utf8.h"
40 #include "localization.h"
41
42 #include "ui.h"
43 #include "ui_data.h"
44
45 UINT_PTR UM_LANGUAGE_MENU_MAX = UM_LANGUAGE_MENU;
46 HIMAGELIST hUpImageList, hDownImageList;
47 extern BOOL use_vds;
48 int update_progress_type = UPT_PERCENT;
49 int advanced_device_section_height, advanced_format_section_height;
50 // (empty) check box width, (empty) drop down width, button height (for and without dropdown match)
51 int cbw, ddw, ddbh = 0, bh = 0;
52 // Row Height, DropDown Height, Main button width, half dropdown width, full dropdown width
53 static int rh, ddh, bw, hw, fw;
54 // See GetFullWidth() for details on how these values are used
55 static int sw, mw, bsw, sbw, ssw, tw, dbw;
56 static WNDPROC progress_original_proc = NULL;
57 static wchar_t wtbtext[2][128];
58 static IAccPropServices* pfaps = NULL;
59
60 /*
61 * The following is used to allocate slots within the progress bar
62 * 0 means unused (no operation or no progress allocated to it)
63 * +n means allocate exactly n bars (n percent of the progress bar)
64 * -n means allocate a weighted slot of n from all remaining
65 * bars. E.g. if 80 slots remain and the sum of all negative entries
66 * is 10, -4 will allocate 4/10*80 = 32 bars (32%) for OP progress
67 */
68 static int nb_slots[OP_MAX];
69 static float slot_end[OP_MAX+1]; // shifted +1 so that we can subtract 1 to OP indexes
70 static float previous_end;
71
72 void SetAccessibleName(HWND hCtrl, const char* name)
73 {
74 const MSAAPROPID props[] = { Name_Property_GUID };
75 wchar_t* wname = utf8_to_wchar(name);
76
77 SetWindowTextW(hCtrl, wname);
78 if (pfaps == NULL)
79 IGNORE_RETVAL(CoCreateInstance(&CLSID_AccPropServices, NULL, CLSCTX_INPROC, &IID_IAccPropServices, (LPVOID)&pfaps));
80 if (pfaps != NULL) {
81 IAccPropServices_ClearHwndProps(pfaps, hCtrl, OBJID_CLIENT, CHILDID_SELF, props, ARRAYSIZE(props));
82 IAccPropServices_SetHwndPropStr(pfaps, hCtrl, OBJID_CLIENT, CHILDID_SELF, Name_Property_GUID, wname);
83 }
84 free(wname);
85 }
86
87 // Set the combo selection according to the data
88 void SetComboEntry(HWND hDlg, int data)
89 {
90 int i, nb_entries = ComboBox_GetCount(hDlg);
91
92 if (nb_entries <= 0)
93 return;
94 for (i = 0; i < nb_entries; i++) {
95 if (ComboBox_GetItemData(hDlg, i) == data) {
96 IGNORE_RETVAL(ComboBox_SetCurSel(hDlg, i));
97 return;
98 }
99 }
100 if (i == nb_entries)
101 IGNORE_RETVAL(ComboBox_SetCurSel(hDlg, 0));
102 }
103
104 // Move a control along the Y axis
105 static __inline void MoveCtrlY(HWND hDlg, int nID, int vertical_shift) {
106 ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, nID), 0, vertical_shift, 0, 0, 1.0f);
107 }
108
109 // https://stackoverflow.com/a/20926332/1069307
110 // https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818.aspx
111 void GetBasicControlsWidth(HWND hDlg)
112 {
113 int checkbox_internal_spacing = 12, dropdown_internal_spacing = 15;
114 RECT rc = { 0, 0, 4, 8 };
115 SIZE sz;
116
117 // Compute base unit sizes since GetDialogBaseUnits() returns garbage data.
118 // See http://support.microsoft.com/kb/125681
119 MapDialogRect(hDlg, &rc);
120 sz.cx = rc.right;
121 sz.cy = rc.bottom;
122
123 // TODO: figure out the specifics of each Windows version
124 if (nWindowsVersion == WINDOWS_10) {
125 checkbox_internal_spacing = 10;
126 dropdown_internal_spacing = 13;
127 }
128
129 // Checkbox and (blank) dropdown widths
130 cbw = MulDiv(checkbox_internal_spacing, sz.cx, 4);
131 ddw = MulDiv(dropdown_internal_spacing, sz.cx, 4);
132
133 // Spacing width between half-length dropdowns (sep) as well as left margin
134 GetWindowRect(GetDlgItem(hDlg, IDC_TARGET_SYSTEM), &rc);
135 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
136 sw = rc.left;
137 GetWindowRect(GetDlgItem(hDlg, IDC_PARTITION_TYPE), &rc);
138 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
139 sw -= rc.right;
140 mw = rc.left;
141
142 // Small button width
143 SendMessage(hSaveToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
144 sbw = sz.cx;
145
146 // Small separator widths and button height
147 GetWindowRect(GetDlgItem(hDlg, IDC_SAVE), &rc);
148 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
149 bh = rc.bottom - rc.top;
150 ssw = rc.left;
151 GetWindowRect(hDeviceList, &rc);
152 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
153 ssw -= rc.right;
154
155 // CSM tooltip separator width
156 GetWindowRect(GetDlgItem(hDlg, IDS_CSM_HELP_TXT), &rc);
157 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
158 tw = rc.left;
159 GetWindowRect(hTargetSystem, &rc);
160 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
161 tw -= rc.right;
162 }
163
164 // Compute the minimum size of the main buttons
165 void GetMainButtonsWidth(HWND hDlg)
166 {
167 unsigned int i;
168 RECT rc;
169 char download[64];
170
171 GetWindowRect(GetDlgItem(hDlg, main_button_ids[0]), &rc);
172 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
173 bw = rc.right - rc.left;
174
175 for (i = 0; i < ARRAYSIZE(main_button_ids); i++) {
176 // Make sure we add extra space for the SELECT split button (i == 0) if Fido is enabled
177 bw = max(bw, GetTextWidth(hDlg, main_button_ids[i]) + ((i == 0) ? (3 * cbw) / 2 : cbw));
178 }
179 // The 'CLOSE' button is also be used to display 'CANCEL' and we sometimes
180 // want to add "DOWNLOAD" into the Select split button => measure that too.
181 bw = max(bw, GetTextSize(GetDlgItem(hDlg, IDCANCEL), lmprintf(MSG_007)).cx + cbw);
182 static_strcpy(download, lmprintf(MSG_040));
183 CharUpperBuffU(download, sizeof(download));
184 bw = max(bw, GetTextSize(GetDlgItem(hDlg, IDC_SELECT), download).cx + (3 * cbw) / 2);
185 }
186
187 // The following goes over the data that gets populated into the half-width dropdowns
188 // (Partition scheme, Target System, Disk ID, File system, Cluster size, Nb passes)
189 // to figure out the minimum width we should allocate.
190 void GetHalfDropwdownWidth(HWND hDlg)
191 {
192 RECT rc;
193 unsigned int i, j, msg_id;
194 char tmp[256];
195
196 // Initialize half width to the UI's default size
197 GetWindowRect(GetDlgItem(hDlg, IDC_PARTITION_TYPE), &rc);
198 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
199 hw = rc.right - rc.left - ddw;
200
201 // "Super Floppy Disk" is the longuest entry in the Partition Scheme dropdown
202 hw = max(hw, GetTextSize(GetDlgItem(hDlg, IDC_PARTITION_TYPE), (char*)sfd_name).cx);
203
204 // This is basically the same as SetClusterSizeLabels() except we're adding (Default) to each entry
205 for (i = 512, j = 1, msg_id = MSG_026; j<MAX_CLUSTER_SIZES; i <<= 1, j++) {
206 if (i > 8192) {
207 i /= 1024;
208 msg_id++;
209 }
210 safe_sprintf(tmp, 64, "%d %s", i, lmprintf(msg_id));
211 hw = max(hw, GetTextSize(GetDlgItem(hDlg, IDC_CLUSTER_SIZE), lmprintf(MSG_030, tmp)).cx);
212 }
213 // We don't go over file systems, because none of them will be longer than "Super Floppy Disk"
214 // We do however go over the BIOS vs UEFI entries, as some of these are translated
215 for (msg_id = MSG_031; msg_id <= MSG_033; msg_id++)
216 hw = max(hw, GetTextSize(GetDlgItem(hDlg, IDC_TARGET_SYSTEM), lmprintf(msg_id)).cx);
217
218 // Just in case, we also do the number of passes
219 for (i = 1; i <= 5; i++) {
220 char* msg = (i == 1) ? lmprintf(MSG_034, 1) : lmprintf(MSG_035, (i == 2) ? 2 : 4, (i == 2) ? "" : lmprintf(MSG_087, flash_type[i - 3]));
221 hw = max(hw, GetTextSize(GetDlgItem(hDlg, IDC_TARGET_SYSTEM), msg).cx);
222 }
223
224 // Finally, we must ensure that we'll have enough space for the 2 checkbox controls
225 // that end up with a half dropdown
226 hw = max(hw, GetTextWidth(hDlg, IDC_RUFUS_MBR) - sw);
227 hw = max(hw, GetTextWidth(hDlg, IDC_BAD_BLOCKS) - sw);
228
229 // Add the width of a blank dropdown
230 hw += ddw;
231 }
232
233 /*
234 * dbw = dialog border width
235 * mw = margin width
236 * fw = full dropdown width
237 * hd = half dropdown width
238 * bsw = boot selection dropdown width
239 * sw = separator width
240 * ssw = small separator width
241 * bw = button width
242 * sbw = small button width
243 *
244 * | fw |
245 * | bsw | ssw | sbw | ssw | bw |
246 * 8 ->|<- 96 ->|<- 24 ->|<- 96 ->|<- 8
247 * mw | hw | sw | hw | mw
248 * | bw | ssw | bw |
249 */
250 void GetFullWidth(HWND hDlg)
251 {
252 RECT rc;
253 int i;
254
255 // Get the dialog border width
256 GetWindowRect(hDlg, &rc);
257 dbw = rc.right - rc.left;
258 GetClientRect(hDlg, &rc);
259 dbw -= rc.right - rc.left;
260
261 // Compute the minimum size needed for the Boot Selection dropdown
262 GetWindowRect(GetDlgItem(hDlg, IDC_BOOT_SELECTION), &rc);
263 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
264
265 bsw = max(rc.right - rc.left, GetTextSize(hBootType, lmprintf(MSG_279)).cx + ddw);
266 bsw = max(bsw, GetTextSize(hBootType, lmprintf(MSG_281, lmprintf(MSG_280))).cx + ddw);
267
268 // Initialize full width to the UI's default size
269 GetWindowRect(GetDlgItem(hDlg, IDS_DEVICE_TXT), &rc);
270 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
271 fw = rc.right - rc.left - ddw;
272
273 // Go through the Image Options for Windows To Go
274 fw = max(fw, GetTextSize(hImageOption, lmprintf(MSG_117)).cx);
275 fw = max(fw, GetTextSize(hImageOption, lmprintf(MSG_118)).cx);
276
277 // Now deal with full length checkbox lines
278 for (i = 0; i<ARRAYSIZE(full_width_checkboxes); i++)
279 fw = max(fw, GetTextWidth(hDlg, full_width_checkboxes[i]));
280
281 // All of the above is for text only, so we need to add dd space
282 fw += ddw;
283
284 // Our min also needs to be longer than 2 half length dropdowns + spacer
285 fw = max(fw, 2 * hw + sw);
286
287 // Now that we have our minimum full width, adjust the button width if needed
288 // Adjust according to min full width
289 bw = max(bw, (fw - 2 * ssw - sw) / 4);
290 // Adjust according to min boot selection width
291 bw = max(bw, (bsw + sbw - sw) / 3);
292
293 // Adjust according to min half width
294 bw = max(bw, (hw / 2) - ssw);
295
296 // Now that our button width is set, we can adjust the rest
297 hw = max(hw, 2 * bw + ssw);
298 fw = max(fw, 2 * hw + sw);
299
300 bsw = max(bsw, fw - bw - 2 * ssw - sbw);
301
302 // TODO: Also pick a few choice messages from info/status
303 }
304
305 void PositionMainControls(HWND hDlg)
306 {
307 RECT rc;
308 HWND hCtrl, hPrevCtrl;
309 SIZE sz;
310 DWORD padding;
311 int i, x, button_fudge = 2;
312
313 // Start by resizing the whole dialog
314 GetWindowRect(hDlg, &rc);
315 // Don't forget to add the dialog border width, since we resize the whole dialog
316 SetWindowPos(hDlg, NULL, -1, -1, fw + 2 * mw + dbw, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
317
318 // Resize the height of the label, persistence size and progress bar to the height of standard dropdowns
319 hCtrl = GetDlgItem(hDlg, IDC_DEVICE);
320 GetWindowRect(hCtrl, &rc);
321 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
322 ddh = rc.bottom - rc.top;
323 ddbh = ddh + button_fudge;
324 bh = max(bh, ddbh);
325 hCtrl = GetDlgItem(hDlg, IDC_LABEL);
326 GetWindowRect(hCtrl, &rc);
327 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
328 SetWindowPos(hCtrl, hAdvancedFormatToolbar, rc.left, rc.top, rc.right - rc.left, ddh, SWP_NOZORDER);
329 hCtrl = GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE);
330 GetWindowRect(hCtrl, &rc);
331 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
332 SetWindowPos(hCtrl, GetDlgItem(hDlg, IDC_PERSISTENCE_SLIDER), rc.left, rc.top, rc.right - rc.left, ddh, SWP_NOZORDER);
333 GetWindowRect(hProgress, &rc);
334 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
335 SetWindowPos(hProgress, hNBPasses, rc.left, rc.top, rc.right - rc.left, ddh, SWP_NOZORDER);
336
337 // Get the height of a typical row
338 hCtrl = GetDlgItem(hDlg, IDS_BOOT_SELECTION_TXT);
339 GetWindowRect(hCtrl, &rc);
340 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
341 rh = rc.top;
342 hCtrl = GetDlgItem(hDlg, IDS_DEVICE_TXT);
343 GetWindowRect(hCtrl, &rc);
344 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
345 rh -= rc.top;
346
347 // Get the height of the advanced options
348 hCtrl = GetDlgItem(hDlg, IDC_LIST_USB_HDD);
349 GetWindowRect(hCtrl, &rc);
350 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
351 advanced_device_section_height = rc.top;
352 hCtrl = GetDlgItem(hDlg, IDC_RUFUS_MBR);
353 GetWindowRect(hCtrl, &rc);
354 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
355 advanced_device_section_height = rc.bottom - advanced_device_section_height;
356
357 hCtrl = GetDlgItem(hDlg, IDC_QUICK_FORMAT);
358 GetWindowRect(hCtrl, &rc);
359 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
360 advanced_format_section_height = rc.top;
361 hCtrl = GetDlgItem(hDlg, IDC_BAD_BLOCKS);
362 GetWindowRect(hCtrl, &rc);
363 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
364 advanced_format_section_height = rc.bottom - advanced_format_section_height;
365
366 // Get the vertical position of the sections text
367 hCtrl = GetDlgItem(hDlg, IDS_DRIVE_PROPERTIES_TXT);
368 GetWindowRect(hCtrl, &rc);
369 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
370 sz = GetTextSize(hCtrl, NULL);
371 section_vpos[0] = rc.top + 2 * sz.cy / 3;
372 hCtrl = GetDlgItem(hDlg, IDS_FORMAT_OPTIONS_TXT);
373 GetWindowRect(hCtrl, &rc);
374 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
375 sz = GetTextSize(hCtrl, NULL);
376 section_vpos[1] = rc.top + 2 * sz.cy / 3;
377 hCtrl = GetDlgItem(hDlg, IDS_STATUS_TXT);
378 GetWindowRect(hCtrl, &rc);
379 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
380 sz = GetTextSize(hCtrl, NULL);
381 section_vpos[2] = rc.top + 2 * sz.cy / 3;
382
383 // Seriously, who designed this bullshit API call where you pass a SIZE
384 // struct but can only retrieve one of cx or cy at a time?!?
385 SendMessage(hMultiToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
386 GetWindowRect(GetDlgItem(hDlg, IDC_ABOUT), &rc);
387 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
388 SetWindowPos(hMultiToolbar, hProgress, rc.left, rc.top, sz.cx, ddbh, 0);
389
390 // Reposition the main buttons
391 for (i = 0; i < ARRAYSIZE(main_button_ids); i++) {
392 hCtrl = GetDlgItem(hDlg, main_button_ids[i]);
393 GetWindowRect(hCtrl, &rc);
394 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
395 x = mw + fw - bw;
396 if (i % 2 == 1)
397 x -= bw + ssw;
398 hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
399 SetWindowPos(hCtrl, hPrevCtrl, x, rc.top, bw, ddbh, 0);
400 }
401
402 // Reposition the Save button
403 hCtrl = GetDlgItem(hDlg, IDC_SAVE);
404 GetWindowRect(hCtrl, &rc);
405 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
406 SendMessage(hSaveToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
407 SendMessage(hSaveToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(sz.cx, ddbh));
408 // Microsoft, how I loathe thee!!!
409 padding = (DWORD)SendMessage(hSaveToolbar, TB_GETPADDING, 0, 0);
410 sz.cx = padding & 0xFFFF;
411 sz.cy = padding >> 16;
412 SendMessage(hSaveToolbar, TB_SETPADDING, 0, MAKELPARAM(sz.cx + 3, sz.cy + 2));
413 SetWindowPos(hSaveToolbar, hDeviceList, mw + fw - sbw, rc.top, sbw, ddbh, 0);
414
415 // Reposition the Hash button
416 hCtrl = GetDlgItem(hDlg, IDC_HASH);
417 GetWindowRect(hCtrl, &rc);
418 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
419 SendMessage(hHashToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
420 SendMessage(hHashToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(sz.cx, ddbh));
421 padding = (DWORD)SendMessage(hHashToolbar, TB_GETPADDING, 0, 0);
422 sz.cx = padding & 0xFFFF;
423 sz.cy = padding >> 16;
424 SendMessage(hHashToolbar, TB_SETPADDING, 0, MAKELPARAM(sz.cx + 3, sz.cy + 2));
425 SetWindowPos(hHashToolbar, hBootType, mw + bsw + ssw, rc.top, sbw, ddbh, 0);
426
427 // Reposition the Persistence slider and resize it to the boot selection width
428 hCtrl = GetDlgItem(hDlg, IDC_PERSISTENCE_SLIDER);
429 GetWindowRect(hCtrl, &rc);
430 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
431 SetWindowPos(hCtrl, hImageOption, mw, rc.top, bsw, rc.bottom - rc.top, 0);
432
433 // Reposition the Persistence Units dropdown (no need to resize)
434 hCtrl = GetDlgItem(hDlg, IDC_PERSISTENCE_UNITS);
435 GetWindowRect(hCtrl, &rc);
436 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
437 sz.cx = fw - (rc.right - rc.left);
438 SetWindowPos(hCtrl, GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE), mw + sz.cx, rc.top, rc.right - rc.left, rc.bottom - rc.top, 0);
439 ShowWindow(hCtrl, SW_HIDE);
440
441 // Reposition and resize the Persistence Size edit
442 hCtrl = GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE);
443 GetWindowRect(hCtrl, &rc);
444 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
445 SetWindowPos(hCtrl, GetDlgItem(hDlg, IDC_PERSISTENCE_SLIDER), mw + bsw + ssw, rc.top, fw - bsw - ssw, rc.bottom - rc.top, 0);
446 EnableWindow(hCtrl, FALSE);
447
448 // Reposition the CSM help tip
449 hCtrl = GetDlgItem(hDlg, IDS_CSM_HELP_TXT);
450 GetWindowRect(hCtrl, &rc);
451 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
452 SetWindowPos(hCtrl, hTargetSystem, mw + fw + tw, rc.top, sbw, rc.bottom - rc.top, 0);
453
454 if (advanced_mode_device) {
455 // Still need to adjust the width of the device selection dropdown
456 GetWindowRect(hDeviceList, &rc);
457 MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
458 SetWindowPos(hDeviceList, GetDlgItem(hDlg, IDS_DEVICE_TXT), rc.left, rc.top, fw - ssw - sbw, rc.bottom - rc.top, 0);
459 }
460
461 // Resize the full width controls
462 for (i = 0; i < ARRAYSIZE(full_width_controls); i++) {
463 hCtrl = GetDlgItem(hDlg, full_width_controls[i]);
464 GetWindowRect(hCtrl, &rc);
465 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
466 hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
467 SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top, fw, rc.bottom - rc.top, 0);
468 }
469
470 // Resize the half drowpdowns
471 for (i = 0; i < ARRAYSIZE(half_width_ids); i++) {
472 hCtrl = GetDlgItem(hDlg, half_width_ids[i]);
473 GetWindowRect(hCtrl, &rc);
474 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
475 // First 5 controls are on the left handside
476 // First 2 controls may overflow into separator
477 hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
478 SetWindowPos(hCtrl, hPrevCtrl, (i < 5) ? rc.left : mw + hw + sw, rc.top,
479 (i <2) ? hw + sw : hw, rc.bottom - rc.top, 0);
480 }
481
482 // Resize the boot selection dropdown
483 hCtrl = GetDlgItem(hDlg, IDC_BOOT_SELECTION);
484 GetWindowRect(hCtrl, &rc);
485 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
486 hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
487 SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top, bsw, rc.bottom - rc.top, 0);
488 }
489
490 static void ResizeDialogs(int shift)
491 {
492 RECT rc;
493 POINT point;
494
495 // Resize the main dialog
496 GetWindowRect(hMainDialog, &rc);
497 point.x = (rc.right - rc.left);
498 point.y = (rc.bottom - rc.top);
499 MoveWindow(hMainDialog, rc.left, rc.top, point.x, point.y + shift, TRUE);
500
501 // Resize the log
502 GetWindowRect(hLogDialog, &rc);
503 point.x = (rc.right - rc.left);
504 point.y = (rc.bottom - rc.top);
505 MoveWindow(hLogDialog, rc.left, rc.top, point.x, point.y + shift, TRUE);
506 MoveCtrlY(hLogDialog, IDC_LOG_CLEAR, shift);
507 MoveCtrlY(hLogDialog, IDC_LOG_SAVE, shift);
508 MoveCtrlY(hLogDialog, IDCANCEL, shift);
509 GetWindowRect(hLog, &rc);
510 point.x = (rc.right - rc.left);
511 point.y = (rc.bottom - rc.top) + shift;
512 SetWindowPos(hLog, NULL, 0, 0, point.x, point.y, SWP_NOZORDER);
513 // Don't forget to scroll the edit to the bottom after resize
514 Edit_Scroll(hLog, 0, Edit_GetLineCount(hLog));
515 }
516
517 // Thanks to Microsoft atrocious DPI handling, we must adjust for low DPI
518 void AdjustForLowDPI(HWND hDlg)
519 {
520 static int ddy = 4;
521 int i, j;
522 RECT rc;
523 HWND hCtrl, hPrevCtrl;
524 int dy = 0;
525
526 if (fScale >= 1.3f)
527 return;
528
529 for (i = 0; i < ARRAYSIZE(adjust_dpi_ids); i++) {
530 dy += ddy;
531 // "...and the other thing I really like about Microsoft's UI handling is how "
532 //."you never have to introduce weird hardcoded constants all over the place, "
533 // "just to make your UI look good...", said NO ONE ever.
534 if (adjust_dpi_ids[i][0] == IDC_QUICK_FORMAT)
535 dy += 1;
536 for (j = 0; j < 5; j++) {
537 if (adjust_dpi_ids[i][j] == 0)
538 break;
539 hCtrl = GetDlgItem(hDlg, adjust_dpi_ids[i][j]);
540 GetWindowRect(hCtrl, &rc);
541 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
542 hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
543 SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top + dy,
544 rc.right - rc.left, rc.bottom - rc.top, 0);
545 }
546 }
547
548 section_vpos[1] += 9 * ddy;
549 section_vpos[2] += 16 * ddy + 1;
550 advanced_device_section_height += 3 * ddy;
551 advanced_format_section_height += 3 * ddy + 1;
552
553 ResizeDialogs(dy + 2 * ddy);
554 InvalidateRect(hDlg, NULL, TRUE);
555 }
556
557 void SetSectionHeaders(HWND hDlg)
558 {
559 RECT rc;
560 HWND hCtrl;
561 SIZE sz;
562 HFONT hf;
563 wchar_t wtmp[128];
564 size_t wlen;
565 int i;
566
567 // Set the section header fonts and resize the static controls accordingly
568 hf = CreateFontA(-MulDiv(14, GetDeviceCaps(GetDC(hMainDialog), LOGPIXELSY), 72), 0, 0, 0,
569 FW_SEMIBOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, PROOF_QUALITY, 0, "Segoe UI");
570
571 for (i = 0; i < ARRAYSIZE(section_control_ids); i++) {
572 SendDlgItemMessageA(hDlg, section_control_ids[i], WM_SETFONT, (WPARAM)hf, TRUE);
573 hCtrl = GetDlgItem(hDlg, section_control_ids[i]);
574 memset(wtmp, 0, sizeof(wtmp));
575 GetWindowTextW(hCtrl, wtmp, ARRAYSIZE(wtmp) - 3);
576 wlen = wcslen(wtmp);
577 wtmp[wlen++] = L' ';
578 wtmp[wlen++] = L' ';
579 SetWindowTextW(hCtrl, wtmp);
580 GetWindowRect(hCtrl, &rc);
581 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
582 sz = GetTextSize(hCtrl, NULL);
583 SetWindowPos(hCtrl, NULL, rc.left, rc.top, sz.cx, sz.cy, SWP_NOZORDER);
584 }
585 }
586
587 // Toggle "advanced" options
588 void ToggleAdvancedDeviceOptions(BOOL enable)
589 {
590 RECT rc;
591 SIZE sz;
592 TBBUTTONINFO button_info;
593 int i, shift = advanced_device_section_height;
594
595 if (!enable)
596 shift = -shift;
597 section_vpos[1] += shift;
598 section_vpos[2] += shift;
599
600 // Toggle the Hide/Show toolbar text
601 utf8_to_wchar_no_alloc(lmprintf((enable) ? MSG_122 : MSG_121, lmprintf(MSG_119)), wtbtext[0], ARRAYSIZE(wtbtext[0]));
602 button_info.cbSize = sizeof(button_info);
603 button_info.dwMask = TBIF_TEXT;
604 button_info.pszText = wtbtext[0];
605 SendMessage(hAdvancedDeviceToolbar, TB_SETBUTTONINFO, (WPARAM)IDC_ADVANCED_DRIVE_PROPERTIES, (LPARAM)&button_info);
606 SendMessage(hAdvancedDeviceToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)((enable) ? hUpImageList : hDownImageList));
607 GetWindowRect(hAdvancedDeviceToolbar, &rc);
608 MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
609 SendMessage(hAdvancedDeviceToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
610 // TB_GETIDEALSIZE may act up and report negative values
611 if (sz.cx < 16)
612 sz.cx = fw;
613 SetWindowPos(hAdvancedDeviceToolbar, hTargetSystem, rc.left, rc.top, sz.cx, rc.bottom - rc.top, 0);
614
615 // Move the controls up or down
616 for (i = 0; i<ARRAYSIZE(advanced_device_move_ids); i++)
617 MoveCtrlY(hMainDialog, advanced_device_move_ids[i], shift);
618
619 // Hide or show the various advanced options
620 for (i = 0; i<ARRAYSIZE(advanced_device_toggle_ids); i++)
621 ShowWindow(GetDlgItem(hMainDialog, advanced_device_toggle_ids[i]), enable ? SW_SHOW : SW_HIDE);
622
623 GetWindowRect(hDeviceList, &rc);
624 MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
625 SetWindowPos(hDeviceList, GetDlgItem(hMainDialog, IDS_DEVICE_TXT), rc.left, rc.top, enable ? fw - ssw - sbw : fw, rc.bottom - rc.top, 0);
626
627 // Resize the main dialog and log window
628 ResizeDialogs(shift);
629
630 // Never hurts to force Windows' hand
631 InvalidateRect(hMainDialog, NULL, TRUE);
632 }
633
634 void ToggleAdvancedFormatOptions(BOOL enable)
635 {
636 RECT rc;
637 SIZE sz;
638 TBBUTTONINFO button_info;
639 int i, shift = advanced_format_section_height;
640
641 if (!enable)
642 shift = -shift;
643 section_vpos[2] += shift;
644
645 // Toggle the Hide/Show toolbar text
646 utf8_to_wchar_no_alloc(lmprintf((enable) ? MSG_122 : MSG_121, lmprintf(MSG_120)), wtbtext[1], ARRAYSIZE(wtbtext[0]));
647 button_info.cbSize = sizeof(button_info);
648 button_info.dwMask = TBIF_TEXT;
649 button_info.pszText = wtbtext[1];
650 SendMessage(hAdvancedFormatToolbar, TB_SETBUTTONINFO, (WPARAM)IDC_ADVANCED_FORMAT_OPTIONS, (LPARAM)&button_info);
651 SendMessage(hAdvancedFormatToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)((enable) ? hUpImageList : hDownImageList));
652 GetWindowRect(hAdvancedFormatToolbar, &rc);
653 MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
654 SendMessage(hAdvancedFormatToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
655 if (sz.cx < 16)
656 sz.cx = fw;
657 SetWindowPos(hAdvancedFormatToolbar, hClusterSize, rc.left, rc.top, sz.cx, rc.bottom - rc.top, 0);
658
659 // Move the controls up or down
660 for (i = 0; i<ARRAYSIZE(advanced_format_move_ids); i++)
661 MoveCtrlY(hMainDialog, advanced_format_move_ids[i], shift);
662
663 // Hide or show the various advanced options
664 for (i = 0; i<ARRAYSIZE(advanced_format_toggle_ids); i++)
665 ShowWindow(GetDlgItem(hMainDialog, advanced_format_toggle_ids[i]), enable ? SW_SHOW : SW_HIDE);
666
667 // Resize the main dialog and log window
668 ResizeDialogs(shift);
669
670 // Never hurts to force Windows' hand
671 InvalidateRect(hMainDialog, NULL, TRUE);
672 }
673
674 // Toggle the display of persistence unit dropdown and resize the size field
675 void TogglePersistenceControls(BOOL display)
676 {
677 RECT rc;
678 HWND hSize, hUnits;
679 LONG_PTR style;
680 LONG width = fw - bsw - ssw;
681 hSize = GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE);
682 hUnits = GetDlgItem(hMainDialog, IDC_PERSISTENCE_UNITS);
683
684 style = GetWindowLongPtr(hSize, GWL_EXSTYLE);
685 if (display)
686 style |= WS_EX_RIGHT;
687 else
688 style &= ~WS_EX_RIGHT;
689 SetWindowLongPtr(hSize, GWL_EXSTYLE, style);
690
691 if (display) {
692 GetWindowRect(hUnits, &rc);
693 MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
694 width -= (rc.right - rc.left) + ssw;
695 }
696
697 GetWindowRect(hSize, &rc);
698 MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
699 SetWindowPos(hSize, GetDlgItem(hMainDialog, IDC_PERSISTENCE_SLIDER), mw + bsw + ssw, rc.top, width, rc.bottom - rc.top, 0);
700
701 EnableWindow(hSize, display ? TRUE : FALSE);
702 EnableWindow(hUnits, display ? TRUE : FALSE);
703 ShowWindow(hUnits, display ? SW_SHOW : SW_HIDE);
704 }
705
706 void SetPersistencePos(uint64_t pos)
707 {
708 char tmp[64];
709
710 if ((boot_type == BT_IMAGE) && (pos != 0)) {
711 TogglePersistenceControls(TRUE);
712 static_sprintf(tmp, "%ld", (LONG)pos);
713 } else {
714 TogglePersistenceControls(FALSE);
715 static_sprintf(tmp, "0 (%s)", lmprintf(MSG_124));
716 }
717 app_changed_size = TRUE;
718 SetWindowTextU(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE), tmp);
719 }
720
721 void SetPersistenceSize(void)
722 {
723 int i, proposed_unit_selection = 0;
724 LONGLONG base_unit = MB;
725 HWND hCtrl;
726 uint64_t max = 0, pos = 0;
727
728 if (ComboBox_GetCurSel(hDeviceList) >= 0) {
729 max = SelectedDrive.DiskSize - img_report.projected_size;
730 persistence_size = min(persistence_size, max);
731 pos = persistence_size;
732
733 // Reset the the Persistence Units dropdown
734 hCtrl = GetDlgItem(hMainDialog, IDC_PERSISTENCE_UNITS);
735 IGNORE_RETVAL(ComboBox_ResetContent(hCtrl));
736 for (i = 0; i < 3; i++) {
737 IGNORE_RETVAL(ComboBox_SetItemData(hCtrl, ComboBox_AddStringU(hCtrl, lmprintf(MSG_022 + i)), i));
738 // If we have more than 7 discrete positions, set this unit as our base
739 if (SelectedDrive.DiskSize > 7 * base_unit)
740 proposed_unit_selection = i;
741 base_unit *= 1024;
742 // Don't allow a base unit unless the drive is at least twice the size of that unit
743 if (SelectedDrive.DiskSize < 2 * base_unit)
744 break;
745 }
746 if (persistence_unit_selection < 0)
747 persistence_unit_selection = proposed_unit_selection;
748
749 IGNORE_RETVAL(ComboBox_SetCurSel(hCtrl, persistence_unit_selection));
750 if ((pos != 0) && (pos < MIN_EXT_SIZE))
751 pos = MIN_EXT_SIZE;
752 pos /= MB;
753 max /= MB;
754 for (i = 0; i < persistence_unit_selection; i++) {
755 pos /= 1024;
756 max /= 1024;
757 }
758
759 }
760
761 hCtrl = GetDlgItem(hMainDialog, IDC_PERSISTENCE_SLIDER);
762 // Wow! Unless you set *all* these redraw WPARAMs to true, the one from
763 // TBM_SETPOS gets completely ignored if the value is zero!
764 SendMessage(hCtrl, TBM_SETRANGEMIN, (WPARAM)TRUE, (LPARAM)0);
765 SendMessage(hCtrl, TBM_SETRANGEMAX, (WPARAM)TRUE, (LPARAM)max);
766 SendMessage(hCtrl, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pos);
767
768 SetPersistencePos(pos);
769 }
770
771 // Toggle the Image Option dropdown (Windows To Go or persistence settings)
772 void ToggleImageOptions(void)
773 {
774 BOOL has_wintogo, has_persistence;
775 uint8_t entry_image_options = image_options;
776 int i, shift = rh;
777
778 has_wintogo = ((boot_type == BT_IMAGE) && (image_path != NULL) && (img_report.is_iso || img_report.is_windows_img) &&
779 (nWindowsVersion >= WINDOWS_8) && (HAS_WINTOGO(img_report)));
780 has_persistence = ((boot_type == BT_IMAGE) && (image_path != NULL) && (img_report.is_iso) && (HAS_PERSISTENCE(img_report)));
781
782 assert(popcnt8(image_options) <= 1);
783
784 // Keep a copy of the "Image Option" text (so that we don't have to duplicate its transation in the .loc)
785 if (image_option_txt[0] == 0)
786 GetWindowTextU(GetDlgItem(hMainDialog, IDS_IMAGE_OPTION_TXT), image_option_txt, sizeof(image_option_txt));
787
788 if ( ((has_wintogo) && !(image_options & IMOP_WINTOGO)) ||
789 ((!has_wintogo) && (image_options & IMOP_WINTOGO)) ) {
790 image_options ^= IMOP_WINTOGO;
791 if (image_options & IMOP_WINTOGO) {
792 // Set the Windows To Go selection in the dropdown
793 IGNORE_RETVAL(ComboBox_SetCurSel(hImageOption, (img_report.is_windows_img || !windows_to_go_selected) ? 0 : 1));
794 }
795 }
796
797 if (((has_persistence) && !(image_options & IMOP_PERSISTENCE)) ||
798 ((!has_persistence) && (image_options & IMOP_PERSISTENCE))) {
799 image_options ^= IMOP_PERSISTENCE;
800 if (image_options & IMOP_PERSISTENCE) {
801 SetWindowTextU(GetDlgItem(hMainDialog, IDS_IMAGE_OPTION_TXT), lmprintf(MSG_123));
802 TogglePersistenceControls(persistence_size != 0);
803 SetPersistenceSize();
804 }
805 }
806
807 if ( ((entry_image_options != 0) && (has_wintogo || has_persistence)) ||
808 ((entry_image_options == 0) && !(has_wintogo || has_persistence)) )
809 shift = 0;
810
811 if (shift != 0) {
812 if (entry_image_options != 0)
813 shift = -shift;
814 section_vpos[1] += shift;
815 section_vpos[2] += shift;
816
817 for (i = 0; i < ARRAYSIZE(image_option_move_ids); i++)
818 MoveCtrlY(hMainDialog, image_option_move_ids[i], shift);
819
820 // Resize the main dialog and log window
821 ResizeDialogs(shift);
822 }
823
824 // Hide or show the boot options
825 for (i = 0; i < ARRAYSIZE(image_option_toggle_ids); i++) {
826 ShowWindow(GetDlgItem(hMainDialog, image_option_toggle_ids[i][0]),
827 (image_options & image_option_toggle_ids[i][1]) ? SW_SHOW : SW_HIDE);
828 }
829 // If you don't force a redraw here, all kind of bad UI artifacts happen...
830 InvalidateRect(hMainDialog, NULL, TRUE);
831 }
832
833 // We need to create the small toolbar buttons first so that we can compute their width
834 void CreateSmallButtons(HWND hDlg)
835 {
836 HIMAGELIST hImageList;
837 HICON hIconSave, hIconHash;
838 int icon_offset = 0, i16 = GetSystemMetrics(SM_CXSMICON);
839 TBBUTTON tbToolbarButtons[1];
840 unsigned char* buffer;
841 DWORD bufsize;
842
843 if (i16 >= 28)
844 icon_offset = 20;
845 else if (i16 >= 20)
846 icon_offset = 10;
847
848 hSaveToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
849 0, 0, 0, 0, hMainDialog, (HMENU)IDC_SAVE_TOOLBAR, hMainInstance, NULL);
850 hImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE | ILC_MIRROR, 1, 0);
851 buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDI_SAVE_16 + icon_offset), _RT_RCDATA, "save icon", &bufsize, FALSE);
852 hIconSave = CreateIconFromResourceEx(buffer, bufsize, TRUE, 0x30000, 0, 0, 0);
853 ImageList_AddIcon(hImageList, hIconSave);
854 DestroyIcon(hIconSave);
855 SendMessage(hSaveToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hImageList);
856 SendMessage(hSaveToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
857 memset(tbToolbarButtons, 0, sizeof(TBBUTTON));
858 tbToolbarButtons[0].idCommand = IDC_SAVE;
859 tbToolbarButtons[0].fsStyle = BTNS_AUTOSIZE;
860 tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
861 tbToolbarButtons[0].iBitmap = 0;
862 SendMessage(hSaveToolbar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)&tbToolbarButtons);
863 SetAccessibleName(hSaveToolbar, lmprintf(MSG_313));
864
865 hHashToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
866 0, 0, 0, 0, hMainDialog, (HMENU)IDC_HASH_TOOLBAR, hMainInstance, NULL);
867 hImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE | ILC_MIRROR, 1, 0);
868 buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDI_HASH_16 + icon_offset), _RT_RCDATA, "hash icon", &bufsize, FALSE);
869 hIconHash = CreateIconFromResourceEx(buffer, bufsize, TRUE, 0x30000, 0, 0, 0);
870 ImageList_AddIcon(hImageList, hIconHash);
871 DestroyIcon(hIconHash);
872 SendMessage(hHashToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hImageList);
873 SendMessage(hHashToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
874 memset(tbToolbarButtons, 0, sizeof(TBBUTTON));
875 tbToolbarButtons[0].idCommand = IDC_HASH;
876 tbToolbarButtons[0].fsStyle = BTNS_AUTOSIZE;
877 tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
878 tbToolbarButtons[0].iBitmap = 0;
879 SendMessage(hHashToolbar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)&tbToolbarButtons);
880 SetAccessibleName(hHashToolbar, lmprintf(MSG_314));
881 }
882
883 static INT_PTR CALLBACK ProgressCallback(HWND hCtrl, UINT message, WPARAM wParam, LPARAM lParam)
884 {
885 HDC hDC;
886 RECT rc, rc2;
887 PAINTSTRUCT ps;
888 SIZE size;
889 LONG full_right;
890 wchar_t winfo[128];
891 static BOOL marquee_mode = FALSE;
892 static uint32_t pos = 0, min = 0, max = 0xFFFF;
893 static COLORREF color = PROGRESS_BAR_NORMAL_COLOR;
894
895 switch (message) {
896
897 case PBM_SETSTATE:
898 switch (wParam) {
899 case PBST_NORMAL:
900 color = PROGRESS_BAR_NORMAL_COLOR;
901 break;
902 case PBST_PAUSED:
903 color = PROGRESS_BAR_PAUSED_COLOR;
904 break;
905 case PBST_ERROR:
906 color = PROGRESS_BAR_ERROR_COLOR;
907 break;
908 }
909 return (INT_PTR)TRUE;
910
911 case PBM_SETRANGE:
912 CallWindowProc(progress_original_proc, hCtrl, message, wParam, lParam);
913 // Don't bother sanity checking min and max: If *you* want to
914 // be an ass about the progress bar range, it's *your* problem.
915 min = (uint32_t)(lParam & 0xFFFF);
916 max = (uint32_t)(lParam >> 16);
917 return (INT_PTR)TRUE;
918
919 case PBM_SETPOS:
920 CallWindowProc(progress_original_proc, hCtrl, message, wParam, lParam);
921 pos = (WORD)wParam;
922 InvalidateRect(hProgress, NULL, TRUE);
923 return (INT_PTR)TRUE;
924
925 case PBM_SETMARQUEE:
926 CallWindowProc(progress_original_proc, hCtrl, message, wParam, lParam);
927 if ((wParam == TRUE) && (!marquee_mode)) {
928 marquee_mode = TRUE;
929 pos = min;
930 color = PROGRESS_BAR_NORMAL_COLOR;
931 SetTimer(hCtrl, TID_MARQUEE_TIMER, MARQUEE_TIMER_REFRESH, NULL);
932 InvalidateRect(hProgress, NULL, TRUE);
933 } else if ((wParam == FALSE) && (marquee_mode)) {
934 marquee_mode = FALSE;
935 KillTimer(hCtrl, TID_MARQUEE_TIMER);
936 pos = min;
937 InvalidateRect(hProgress, NULL, TRUE);
938 }
939 return (INT_PTR)TRUE;
940
941 case WM_TIMER:
942 if ((wParam == TID_MARQUEE_TIMER) && marquee_mode) {
943 pos += max((max - min) / (1000 / MARQUEE_TIMER_REFRESH), 1);
944 if ((pos > max) || (pos < min))
945 pos = min;
946 InvalidateRect(hProgress, NULL, TRUE);
947 return (INT_PTR)TRUE;
948 }
949 return (INT_PTR)FALSE;
950
951 case WM_PAINT:
952 hDC = BeginPaint(hCtrl, &ps);
953 GetClientRect(hCtrl, &rc);
954 rc2 = rc;
955 InflateRect(&rc, -1, -1);
956 SelectObject(hDC, GetStockObject(DC_PEN));
957 SelectObject(hDC, GetStockObject(NULL_BRUSH));
958 // TODO: Handle SetText message so we can avoid this call
959 GetWindowTextW(hProgress, winfo, ARRAYSIZE(winfo));
960 SelectObject(hDC, hInfoFont);
961 GetTextExtentPoint32(hDC, winfo, (int)wcslen(winfo), &size);
962 if (size.cx > rc.right)
963 size.cx = rc.right;
964 if (size.cy > rc.bottom)
965 size.cy = rc.bottom;
966 full_right = rc.right;
967 if (marquee_mode) {
968 // Optional first segment
969 if (pos + ((max - min) / 5) > max) {
970 rc.right = MulDiv(pos + ((max - min) / 5) - max, rc.right, max - min);
971 SetTextColor(hDC, PROGRESS_BAR_INVERTED_TEXT_COLOR);
972 SetBkColor(hDC, color);
973 ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
974 ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
975 rc.left = rc.right;
976 rc.right = full_right;
977 }
978 // Optional second segment
979 if (pos > min) {
980 rc.right = MulDiv(pos - min, rc.right, max - min);
981 SetTextColor(hDC, PROGRESS_BAR_NORMAL_TEXT_COLOR);
982 SetBkColor(hDC, PROGRESS_BAR_BACKGROUND_COLOR);
983 ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
984 ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
985 rc.left = rc.right;
986 rc.right = full_right;
987 }
988 // Second to last segment
989 rc.right = MulDiv(pos - min + ((max - min) / 5), rc.right, max - min);
990 SetTextColor(hDC, PROGRESS_BAR_INVERTED_TEXT_COLOR);
991 SetBkColor(hDC, color);
992 ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
993 ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
994 } else {
995 // First segment
996 rc.right = (pos > min) ? MulDiv(pos - min, rc.right, max - min) : rc.left;
997 SetTextColor(hDC, PROGRESS_BAR_INVERTED_TEXT_COLOR);
998 SetBkColor(hDC, color);
999 ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
1000 ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
1001 }
1002 // Last segment
1003 rc.left = rc.right;
1004 rc.right = full_right;
1005 SetTextColor(hDC, PROGRESS_BAR_NORMAL_TEXT_COLOR);
1006 SetBkColor(hDC, PROGRESS_BAR_BACKGROUND_COLOR);
1007 ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
1008 ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
1009 // Bounding rectangle
1010 SetDCPenColor(hDC, PROGRESS_BAR_BOX_COLOR);
1011 Rectangle(hDC, rc2.left, rc2.top, rc2.right, rc2.bottom);
1012 EndPaint(hCtrl, &ps);
1013 return (INT_PTR)TRUE;
1014 }
1015
1016 return CallWindowProc(progress_original_proc, hCtrl, message, wParam, lParam);
1017 }
1018
1019 void CreateAdditionalControls(HWND hDlg)
1020 {
1021 HINSTANCE hDll;
1022 HIMAGELIST hToolbarImageList;
1023 HICON hIcon, hIconUp, hIconDown;
1024 RECT rc;
1025 SIZE sz;
1026 int icon_offset = 0, i, i16, s16, toolbar_dx = -4 - ((fScale > 1.49f) ? 1 : 0) - ((fScale > 1.99f) ? 1 : 0);
1027 TBBUTTON tbToolbarButtons[7];
1028 unsigned char* buffer;
1029 DWORD bufsize;
1030
1031 s16 = i16 = GetSystemMetrics(SM_CXSMICON);
1032 if (s16 >= 54)
1033 s16 = 64;
1034 else if (s16 >= 40)
1035 s16 = 48;
1036 else if (s16 >= 28)
1037 s16 = 32;
1038 else if (s16 >= 20)
1039 s16 = 24;
1040 if (i16 >= 28)
1041 icon_offset = 20;
1042 else if (i16 >= 20)
1043 icon_offset = 10;
1044
1045 // Fetch the up and down expand icons for the advanced options toolbar
1046 hDll = GetLibraryHandle("ComDlg32");
1047 hIconDown = (HICON)LoadImage(hDll, MAKEINTRESOURCE(577), IMAGE_ICON, s16, s16, LR_DEFAULTCOLOR | LR_SHARED);
1048 hIconUp = (HICON)LoadImage(hDll, MAKEINTRESOURCE(578), IMAGE_ICON, s16, s16, LR_DEFAULTCOLOR | LR_SHARED);
1049 // Fallback to using Shell32 if we can't locate the icons we want in ComDlg32
1050 hDll = GetLibraryHandle("Shell32");
1051 if (hIconUp == NULL)
1052 hIconUp = (HICON)LoadImage(hDll, MAKEINTRESOURCE(16749), IMAGE_ICON, s16, s16, LR_DEFAULTCOLOR | LR_SHARED);
1053 if (hIconDown == NULL)
1054 hIconDown = (HICON)LoadImage(hDll, MAKEINTRESOURCE(16750), IMAGE_ICON, s16, s16, LR_DEFAULTCOLOR | LR_SHARED);
1055 hUpImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE, 1, 0);
1056 hDownImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE, 1, 0);
1057 ImageList_AddIcon(hUpImageList, hIconUp);
1058 ImageList_AddIcon(hDownImageList, hIconDown);
1059
1060 // Create the advanced options toolbars
1061 memset(wtbtext, 0, sizeof(wtbtext));
1062 utf8_to_wchar_no_alloc(lmprintf((advanced_mode_device) ? MSG_122 : MSG_121, lmprintf(MSG_119)), wtbtext[0], ARRAYSIZE(wtbtext[0]));
1063 hAdvancedDeviceToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
1064 0, 0, 0, 0, hMainDialog, (HMENU)IDC_ADVANCED_DEVICE_TOOLBAR, hMainInstance, NULL);
1065 SendMessage(hAdvancedDeviceToolbar, CCM_SETVERSION, (WPARAM)6, 0);
1066 memset(tbToolbarButtons, 0, sizeof(TBBUTTON));
1067 tbToolbarButtons[0].idCommand = IDC_ADVANCED_DRIVE_PROPERTIES;
1068 tbToolbarButtons[0].fsStyle = BTNS_SHOWTEXT | BTNS_AUTOSIZE;
1069 tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
1070 tbToolbarButtons[0].iString = (INT_PTR)wtbtext[0];
1071 tbToolbarButtons[0].iBitmap = 0;
1072 SendMessage(hAdvancedDeviceToolbar, TB_SETIMAGELIST, 0, (LPARAM)hUpImageList);
1073 SendMessage(hAdvancedDeviceToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
1074 SendMessage(hAdvancedDeviceToolbar, TB_ADDBUTTONS, 1, (LPARAM)&tbToolbarButtons);
1075 GetWindowRect(GetDlgItem(hDlg, IDC_ADVANCED_DRIVE_PROPERTIES), &rc);
1076 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
1077 SendMessage(hAdvancedDeviceToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
1078 // Yeah, so, like, TB_GETIDEALSIZE totally super doesn't work on Windows 7, for low zoom factor and when compiled with MSVC...
1079 if (sz.cx < 16)
1080 sz.cx = fw;
1081 SetWindowPos(hAdvancedDeviceToolbar, hTargetSystem, rc.left + toolbar_dx, rc.top, sz.cx, rc.bottom - rc.top, 0);
1082 SetAccessibleName(hAdvancedDeviceToolbar, lmprintf(MSG_119));
1083
1084 utf8_to_wchar_no_alloc(lmprintf((advanced_mode_format) ? MSG_122 : MSG_121, lmprintf(MSG_120)), wtbtext[1], ARRAYSIZE(wtbtext[1]));
1085 hAdvancedFormatToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
1086 0, 0, 0, 0, hMainDialog, (HMENU)IDC_ADVANCED_FORMAT_TOOLBAR, hMainInstance, NULL);
1087 SendMessage(hAdvancedFormatToolbar, CCM_SETVERSION, (WPARAM)6, 0);
1088 memset(tbToolbarButtons, 0, sizeof(TBBUTTON));
1089 tbToolbarButtons[0].idCommand = IDC_ADVANCED_FORMAT_OPTIONS;
1090 tbToolbarButtons[0].fsStyle = BTNS_SHOWTEXT | BTNS_AUTOSIZE;
1091 tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
1092 tbToolbarButtons[0].iString = (INT_PTR)wtbtext[1];
1093 tbToolbarButtons[0].iBitmap = 0;
1094 SendMessage(hAdvancedFormatToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hUpImageList);
1095 SendMessage(hAdvancedFormatToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
1096 SendMessage(hAdvancedFormatToolbar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)&tbToolbarButtons);
1097 GetWindowRect(GetDlgItem(hDlg, IDC_ADVANCED_FORMAT_OPTIONS), &rc);
1098 MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
1099 SendMessage(hAdvancedFormatToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
1100 if (sz.cx < 16)
1101 sz.cx = fw;
1102 SetWindowPos(hAdvancedFormatToolbar, hClusterSize, rc.left + toolbar_dx, rc.top, sz.cx, rc.bottom - rc.top, 0);
1103 SetAccessibleName(hAdvancedFormatToolbar, lmprintf(MSG_120));
1104
1105 // Create the multi toolbar
1106 hMultiToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
1107 0, 0, 0, 0, hMainDialog, (HMENU)IDC_MULTI_TOOLBAR, hMainInstance, NULL);
1108 hToolbarImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE, 8, 0);
1109 for (i = 0; i < ARRAYSIZE(multitoolbar_icons); i++) {
1110 buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(multitoolbar_icons[i] + icon_offset),
1111 _RT_RCDATA, "toolbar icon", &bufsize, FALSE);
1112 hIcon = CreateIconFromResourceEx(buffer, bufsize, TRUE, 0x30000, 0, 0, 0);
1113 // Mirror the "world" icon on RTL since we can't use an ImageList mirroring flag for that...
1114 if (right_to_left_mode && (i == 0))
1115 hIcon = CreateMirroredIcon(hIcon);
1116 ImageList_AddIcon(hToolbarImageList, hIcon);
1117 DestroyIcon(hIcon);
1118 }
1119 SendMessage(hMultiToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hToolbarImageList);
1120 SendMessage(hMultiToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
1121 memset(tbToolbarButtons, 0, sizeof(TBBUTTON) * ARRAYSIZE(tbToolbarButtons));
1122 tbToolbarButtons[0].idCommand = IDC_LANG;
1123 tbToolbarButtons[0].fsStyle = BTNS_BUTTON;
1124 tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
1125 tbToolbarButtons[0].iBitmap = 0;
1126 tbToolbarButtons[1].fsStyle = BTNS_AUTOSIZE;
1127 tbToolbarButtons[1].fsState = TBSTATE_INDETERMINATE;
1128 tbToolbarButtons[1].iBitmap = I_IMAGENONE;
1129 tbToolbarButtons[1].iString = (fScale < 1.5f) ? (INT_PTR)L"" : (INT_PTR)L" ";
1130 tbToolbarButtons[2].idCommand = IDC_ABOUT;
1131 tbToolbarButtons[2].fsStyle = BTNS_BUTTON;
1132 tbToolbarButtons[2].fsState = TBSTATE_ENABLED;
1133 tbToolbarButtons[2].iBitmap = 1;
1134 tbToolbarButtons[3].fsStyle = BTNS_AUTOSIZE;
1135 tbToolbarButtons[3].fsState = TBSTATE_INDETERMINATE;
1136 tbToolbarButtons[3].iBitmap = I_IMAGENONE;
1137 tbToolbarButtons[3].iString = (fScale < 1.5f) ? (INT_PTR)L"" : (INT_PTR)L" ";
1138 tbToolbarButtons[4].idCommand = IDC_SETTINGS;
1139 tbToolbarButtons[4].fsStyle = BTNS_BUTTON;
1140 tbToolbarButtons[4].fsState = TBSTATE_ENABLED;
1141 tbToolbarButtons[4].iBitmap = 2;
1142 tbToolbarButtons[5].fsStyle = BTNS_AUTOSIZE;
1143 tbToolbarButtons[5].fsState = TBSTATE_INDETERMINATE;
1144 tbToolbarButtons[5].iBitmap = I_IMAGENONE;
1145 tbToolbarButtons[5].iString = (fScale < 1.5f) ? (INT_PTR)L"" : (INT_PTR)L" ";
1146 tbToolbarButtons[6].idCommand = IDC_LOG;
1147 tbToolbarButtons[6].fsStyle = BTNS_BUTTON;
1148 tbToolbarButtons[6].fsState = TBSTATE_ENABLED;
1149 tbToolbarButtons[6].iBitmap = 3;
1150 SendMessage(hMultiToolbar, TB_ADDBUTTONS, (WPARAM)7, (LPARAM)&tbToolbarButtons);
1151 SendMessage(hMultiToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(i16, ddbh));
1152 SetAccessibleName(hMultiToolbar, lmprintf(MSG_315));
1153
1154 // Subclass the progress bar so that we can write on it
1155 progress_original_proc = (WNDPROC)SetWindowLongPtr(hProgress, GWLP_WNDPROC, (LONG_PTR)ProgressCallback);
1156 }
1157
1158 // Set up progress bar real estate allocation
1159 void InitProgress(BOOL bOnlyFormat)
1160 {
1161 int i;
1162 float last_end = 0.0f, slots_discrete = 0.0f, slots_analog = 0.0f;
1163
1164 memset(nb_slots, 0, sizeof(nb_slots));
1165 memset(slot_end, 0, sizeof(slot_end));
1166 previous_end = 0.0f;
1167
1168 if (bOnlyFormat) {
1169 nb_slots[OP_FORMAT] = -1;
1170 } else {
1171 nb_slots[OP_ANALYZE_MBR] = 1;
1172 if (IsChecked(IDC_BAD_BLOCKS)) {
1173 nb_slots[OP_BADBLOCKS] = -1;
1174 }
1175 if (boot_type != BT_NON_BOOTABLE) {
1176 // 1 extra slot for PBR writing
1177 switch (selection_default) {
1178 case BT_MSDOS:
1179 nb_slots[OP_FILE_COPY] = 3 + 1;
1180 break;
1181 case BT_FREEDOS:
1182 nb_slots[OP_FILE_COPY] = 5 + 1;
1183 break;
1184 case BT_IMAGE:
1185 nb_slots[OP_FILE_COPY] = (img_report.is_iso || img_report.is_windows_img) ? -1 : 0;
1186 break;
1187 default:
1188 nb_slots[OP_FILE_COPY] = 2 + 1;
1189 break;
1190 }
1191 }
1192 if (selection_default == BT_IMAGE && !(img_report.is_iso || img_report.is_windows_img)) {
1193 nb_slots[OP_FORMAT] = -1;
1194 } else {
1195 nb_slots[OP_ZERO_MBR] = 1;
1196 nb_slots[OP_PARTITION] = 1;
1197 nb_slots[OP_FIX_MBR] = 1;
1198 nb_slots[OP_CREATE_FS] = (use_vds) ? 2 :
1199 nb_steps[ComboBox_GetCurItemData(hFileSystem)];
1200 // So, yeah, if you're doing slow format, or using Large FAT32, and have persistence, you'll see
1201 // the progress bar revert during format on account that we reuse the same operation for both
1202 // partitions. Maybe one day I'll be bothered to handle two separate OP_FORMAT ops...
1203 if ((!IsChecked(IDC_QUICK_FORMAT)) || (persistence_size != 0) || (fs_type >= FS_EXT2) ||
1204 ((fs_type == FS_FAT32) && ((SelectedDrive.DiskSize >= LARGE_FAT32_SIZE) || (force_large_fat32)))) {
1205 nb_slots[OP_FORMAT] = -1;
1206 nb_slots[OP_CREATE_FS] = 0;
1207 }
1208 nb_slots[OP_FINALIZE] = ((selection_default == BT_IMAGE) && (fs_type == FS_NTFS)) ? 3 : 2;
1209 }
1210 }
1211
1212 for (i = 0; i < OP_MAX; i++) {
1213 if (nb_slots[i] > 0) {
1214 slots_discrete += nb_slots[i] * 1.0f;
1215 }
1216 if (nb_slots[i] < 0) {
1217 slots_analog += nb_slots[i] * 1.0f;
1218 }
1219 }
1220
1221 for (i = 0; i < OP_MAX; i++) {
1222 if (nb_slots[i] == 0) {
1223 slot_end[i + 1] = last_end;
1224 } else if (nb_slots[i] > 0) {
1225 slot_end[i + 1] = last_end + (1.0f * nb_slots[i]);
1226 } else if (nb_slots[i] < 0) {
1227 slot_end[i + 1] = last_end + (((100.0f - slots_discrete) * nb_slots[i]) / slots_analog);
1228 }
1229 last_end = slot_end[i + 1];
1230 }
1231
1232 // If there's no analog, adjust our discrete ends to fill the whole bar
1233 if (slots_analog == 0.0f) {
1234 for (i = 0; i < OP_MAX; i++) {
1235 slot_end[i + 1] *= 100.0f / slots_discrete;
1236 }
1237 }
1238 }
1239
1240 // Position the progress bar within each operation range
1241 void UpdateProgress(int op, float percent)
1242 {
1243 int pos;
1244 static uint64_t LastRefresh = 0;
1245
1246 if ((op < 0) || (op >= OP_MAX)) {
1247 duprintf("UpdateProgress: invalid op %d\n", op);
1248 return;
1249 }
1250 if (percent > 100.1f) {
1251 // duprintf("UpdateProgress(%d): invalid percentage %0.2f\n", op, percent);
1252 return;
1253 }
1254 if ((percent < 0.0f) && (nb_slots[op] <= 0)) {
1255 duprintf("UpdateProgress(%d): error negative percentage sent for negative slot value\n", op);
1256 return;
1257 }
1258 if (nb_slots[op] == 0)
1259 return;
1260 if (previous_end < slot_end[op]) {
1261 previous_end = slot_end[op];
1262 }
1263
1264 if (percent < 0.0f) {
1265 // Negative means advance one slot (1.0%) - requires a positive slot allocation
1266 previous_end += (slot_end[op + 1] - slot_end[op]) / (1.0f * nb_slots[op]);
1267 pos = (int)(previous_end / 100.0f * MAX_PROGRESS);
1268 } else {
1269 pos = (int)((previous_end + ((slot_end[op + 1] - previous_end) * (percent / 100.0f))) / 100.0f * MAX_PROGRESS);
1270 }
1271 if (pos > MAX_PROGRESS) {
1272 duprintf("UpdateProgress(%d): rounding error - pos %d is greater than %d", op, pos, MAX_PROGRESS);
1273 pos = MAX_PROGRESS;
1274 }
1275
1276 // Reduce the refresh rate, to avoid weird effects on the sliding part of progress bar
1277 if (GetTickCount64() > LastRefresh + (2 * MAX_REFRESH)) {
1278 LastRefresh = GetTickCount64();
1279 SendMessage(hProgress, PBM_SETPOS, (WPARAM)pos, 0);
1280 SetTaskbarProgressValue(pos, MAX_PROGRESS);
1281 }
1282 }
1283
1284 /*
1285 * The following is taken from GNU wget (progress.c)
1286 */
1287 struct bar_progress {
1288 uint64_t total_length; // expected total byte count when the download finishes
1289 uint64_t count; // bytes downloaded so far
1290 uint64_t last_screen_update; // time of the last screen update, measured since the beginning of download.
1291 uint64_t dltime; // download time so far
1292 // Keep track of recent download speeds.
1293 struct bar_progress_hist {
1294 uint64_t pos;
1295 uint64_t times[SPEED_HISTORY_SIZE];
1296 uint64_t bytes[SPEED_HISTORY_SIZE];
1297 // The sum of times and bytes respectively, maintained for efficiency.
1298 uint64_t total_time;
1299 uint64_t total_bytes;
1300 } hist;
1301 uint64_t recent_start; // timestamp of beginning of current position.
1302 uint64_t recent_bytes; // bytes downloaded so far.
1303 BOOL stalled; // set when no data arrives for longer than STALL_START_TIME, then reset when new data arrives.
1304
1305 // The following are used to make sure that ETA information doesn't flicker.
1306 uint64_t last_eta_time; // time of the last update to download speed and ETA, measured since the beginning of download.
1307 int last_eta_value;
1308 };
1309
1310 // This code attempts to maintain the notion of a "current" download speed, over the course
1311 // of no less than 3s. (Shorter intervals produce very erratic results.)
1312 //
1313 // To do so, it samples the speed in 150ms intervals and stores the recorded samples in a
1314 // FIFO history ring. The ring stores no more than 20 intervals, hence the history covers
1315 // the period of at least three seconds and at most 20 reads into the past. This method
1316 // should produce reasonable results for downloads ranging from very slow to very fast.
1317 //
1318 // The idea is that for fast downloads, we get the speed over exactly the last three seconds.
1319 // For slow downloads (where a network read takes more than 150ms to complete), we get the
1320 // speed over a larger time period, as large as it takes to complete twenty reads. This is
1321 // good because slow downloads tend to fluctuate more and a 3-second average would be too
1322 // erratic.
1323 static void bar_update(struct bar_progress* bp, uint64_t howmuch, uint64_t dltime)
1324 {
1325 struct bar_progress_hist* hist = &bp->hist;
1326 uint64_t recent_age = dltime - bp->recent_start;
1327
1328 // Update the download count.
1329 bp->recent_bytes += howmuch;
1330
1331 // For very small time intervals, we return after having updated the
1332 // "recent" download count. When its age reaches or exceeds minimum
1333 // sample time, it will be recorded in the history ring.
1334 if (recent_age < SPEED_SAMPLE_MIN)
1335 return;
1336
1337 if (howmuch == 0) {
1338 // If we're not downloading anything, we might be stalling,
1339 // i.e. not downloading anything for an extended period of time.
1340 // Since 0-reads do not enter the history ring, recent_age
1341 // effectively measures the time since last read.
1342 if (recent_age >= STALL_START_TIME) {
1343 // If we're stalling, reset the ring contents because it's
1344 // stale and because it will make bar_update stop printing
1345 // the (bogus) current bandwidth.
1346 bp->stalled = TRUE;
1347 memset(hist, 0, sizeof(struct bar_progress_hist));
1348 bp->recent_bytes = 0;
1349 }
1350 return;
1351 }
1352
1353 // We now have a non-zero amount of to store to the speed ring.
1354
1355 // If the stall status was acquired, reset it.
1356 if (bp->stalled) {
1357 bp->stalled = FALSE;
1358 // "recent_age" includes the entire stalled period, which
1359 // could be very long. Don't update the speed ring with that
1360 // value because the current bandwidth would start too small.
1361 // Start with an arbitrary (but more reasonable) time value and
1362 // let it level out.
1363 recent_age = 1000;
1364 }
1365
1366 // Store "recent" bytes and download time to history ring at the position POS.
1367
1368 // To correctly maintain the totals, first invalidate existing data
1369 // (least recent in time) at this position. */
1370 hist->total_time -= hist->times[hist->pos];
1371 hist->total_bytes -= hist->bytes[hist->pos];
1372
1373 // Now store the new data and update the totals.
1374 hist->times[hist->pos] = recent_age;
1375 hist->bytes[hist->pos] = bp->recent_bytes;
1376 hist->total_time += recent_age;
1377 hist->total_bytes += bp->recent_bytes;
1378
1379 // Start a new "recent" period.
1380 bp->recent_start = dltime;
1381 bp->recent_bytes = 0;
1382
1383 // Advance the current ring position.
1384 if (++hist->pos == SPEED_HISTORY_SIZE)
1385 hist->pos = 0;
1386 }
1387
1388 // This updates the progress bar as well as the data displayed on it so that we can
1389 // display percentage completed, rate of transfer and estimated remaining duration.
1390 // During init (op = OP_INIT) an optional HWND can be passed on which to look for
1391 // a progress bar. Part of the code (eta, speed) comes from GNU wget.
1392 void UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t total)
1393 {
1394 static int last_update_progress_type = UPT_PERCENT;
1395 static struct bar_progress bp = { 0 };
1396 HWND hProgressDialog = (HWND)(uintptr_t)processed;
1397 static HWND hProgressBar = NULL;
1398 static uint64_t start_time = 0, last_refresh = 0;
1399 uint64_t speed = 0, current_time = GetTickCount64();
1400 double percent = 0.0;
1401 char msg_data[128];
1402 static BOOL bNoAltMode = FALSE;
1403
1404 if (op == OP_INIT) {
1405 start_time = current_time - 1;
1406 last_refresh = 0;
1407 last_update_progress_type = UPT_PERCENT;
1408 percent = 0.0f;
1409 speed = 0;
1410 memset(&bp, 0, sizeof(bp));
1411 bp.total_length = total;
1412 hProgressBar = NULL;
1413 bNoAltMode = (BOOL)msg;
1414 if (hProgressDialog != NULL) {
1415 // Use the progress control provided, if any
1416 hProgressBar = GetDlgItem(hProgressDialog, IDC_PROGRESS);
1417 if (hProgressBar != NULL) {
1418 SendMessage(hProgressBar, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0);
1419 SendMessage(hProgressBar, PBM_SETMARQUEE, FALSE, 0);
1420 SendMessage(hProgressBar, PBM_SETPOS, 0, 0);
1421 }
1422 SendMessage(hProgressDialog, UM_PROGRESS_INIT, 0, 0);
1423 }
1424 } else if ((hProgressBar != NULL) || (op > 0)) {
1425 uint64_t dl_total_time = current_time - start_time;
1426 uint64_t howmuch = processed - bp.count;
1427 bp.count = processed;
1428 bp.total_length = total;
1429 if (bp.count > bp.total_length)
1430 bp.total_length = bp.count;
1431 if (bp.total_length > 0)
1432 percent = (100.0f * bp.count) / (1.0f * bp.total_length);
1433 else
1434 percent = 0.0f;
1435
1436 if ((bp.hist.total_time > 999) && (bp.hist.total_bytes != 0)) {
1437 // Calculate the download speed using the history ring and
1438 // recent data that hasn't made it to the ring yet.
1439 uint64_t dlquant = bp.hist.total_bytes + bp.recent_bytes;
1440 uint64_t dltime = bp.hist.total_time + (dl_total_time - bp.recent_start);
1441 speed = (dltime == 0) ? 0 : (dlquant * 1000) / dltime;
1442 } else {
1443 speed = 0;
1444 }
1445 bar_update(&bp, howmuch, dl_total_time);
1446
1447 if (bNoAltMode)
1448 update_progress_type = UPT_PERCENT;
1449 switch (update_progress_type) {
1450 case UPT_SPEED:
1451 if (speed != 0)
1452 static_sprintf(msg_data, "%s/s", SizeToHumanReadable(speed, FALSE, FALSE));
1453 else
1454 static_sprintf(msg_data, "---");
1455 break;
1456 case UPT_ETA:
1457 if ((bp.total_length > 0) && (bp.count > 0) && (dl_total_time > 3000)) {
1458 uint32_t eta = 0;
1459
1460 // Don't change the value of ETA more than approximately once
1461 // per second; doing so would cause flashing without providing
1462 // any value to the user.
1463 if ((bp.total_length != processed) && (bp.last_eta_value != 0) &&
1464 (dl_total_time - bp.last_eta_time < ETA_REFRESH_INTERVAL)) {
1465 eta = bp.last_eta_value;
1466 } else {
1467 // Calculate ETA using the average download speed to predict
1468 // the future speed. If you want to use a speed averaged
1469 // over a more recent period, replace dl_total_time with
1470 // hist->total_time and bp->count with hist->total_bytes.
1471 // I found that doing that results in a very jerky and
1472 // ultimately unreliable ETA.
1473 uint64_t bytes_remaining = bp.total_length - processed;
1474 double d_eta = (dl_total_time / 1000.0) * (bytes_remaining * 1.0) / (bp.count * 1.0);
1475 if (d_eta >= INT_MAX - 1)
1476 goto skip_eta;
1477 eta = (uint32_t)(d_eta + 0.5);
1478 bp.last_eta_value = eta;
1479 bp.last_eta_time = dl_total_time;
1480 }
1481 static_sprintf(msg_data, "%d:%02d:%02d", eta / 3600, (uint16_t)((eta % 3600) / 60), (uint16_t)(eta % 60));
1482 } else {
1483 skip_eta:
1484 static_sprintf(msg_data, "-:--:--");
1485 }
1486 break;
1487 default:
1488 static_sprintf(msg_data, "%0.1f%%", percent);
1489 break;
1490 }
1491 if ((bp.count == bp.total_length) || (current_time > last_refresh + MAX_REFRESH)) {
1492 if (op < 0) {
1493 SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS * percent / 100.0f), 0);
1494 if (op == OP_NOOP_WITH_TASKBAR)
1495 SetTaskbarProgressValue((ULONGLONG)(MAX_PROGRESS * percent / 100.0f), MAX_PROGRESS);
1496 } else {
1497 UpdateProgress(op, (float)percent);
1498 }
1499 if ((msg >= 0) && ((current_time > bp.last_screen_update + SCREEN_REFRESH_INTERVAL) ||
1500 (last_update_progress_type != update_progress_type) || (bp.count == bp.total_length))) {
1501 PrintInfo(0, msg, msg_data);
1502 bp.last_screen_update = current_time;
1503 }
1504 last_refresh = current_time;
1505 }
1506 last_update_progress_type = update_progress_type;
1507 }
1508 }
1509
1510 void ShowLanguageMenu(RECT rcExclude)
1511 {
1512 TPMPARAMS tpm;
1513 HMENU menu;
1514 RECT rc;
1515 LONG nb_items = 1, adjust = 0;
1516 loc_cmd* lcmd = NULL;
1517 char lang[256];
1518 char *search = "()";
1519 char *l, *r, *str;
1520
1521 UM_LANGUAGE_MENU_MAX = UM_LANGUAGE_MENU;
1522 menu = CreatePopupMenu();
1523 list_for_each_entry(lcmd, &locale_list, loc_cmd, list) {
1524 // The appearance of LTR languages must be fixed for RTL menus
1525 if ((right_to_left_mode) && (!(lcmd->ctrl_id & LOC_RIGHT_TO_LEFT))) {
1526 str = safe_strdup(lcmd->txt[1]);
1527 l = strtok(str, search);
1528 r = strtok(NULL, search);
1529 static_sprintf(lang, LEFT_TO_RIGHT_EMBEDDING "(%s) " POP_DIRECTIONAL_FORMATTING "%s", r, l);
1530 safe_free(str);
1531 } else {
1532 static_strcpy(lang, lcmd->txt[1]);
1533 }
1534 InsertMenuU(menu, -1, MF_BYPOSITION | ((selected_locale == lcmd) ? MF_CHECKED : 0), UM_LANGUAGE_MENU_MAX++, lang);
1535 nb_items++;
1536 }
1537
1538 // Empirical adjust if we have a small enough number of languages to select
1539 if (nb_items < 20) {
1540 GetWindowRect(hMultiToolbar, &rc);
1541 MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
1542 adjust = rc.top - (nb_items * ddh) / 2;
1543 }
1544
1545 // Open the menu such that it doesn't overlap the specified rect
1546 tpm.cbSize = sizeof(TPMPARAMS);
1547 tpm.rcExclude = rcExclude;
1548 TrackPopupMenuEx(menu, 0,
1549 // In RTL languages, the menu should be placed at the bottom-right of the rect
1550 right_to_left_mode ? rcExclude.right : rcExclude.left,
1551 rcExclude.bottom + adjust, hMainDialog, &tpm);
1552 DestroyMenu(menu);
1553 }
1554
1555 void SetPassesTooltip(void)
1556 {
1557 const unsigned int pattern[BADLOCKS_PATTERN_TYPES][BADBLOCK_PATTERN_COUNT] =
1558 { BADBLOCK_PATTERN_ONE_PASS, BADBLOCK_PATTERN_TWO_PASSES, BADBLOCK_PATTERN_SLC,
1559 BADCLOCK_PATTERN_MLC, BADBLOCK_PATTERN_TLC };
1560 int sel = ComboBox_GetCurSel(hNBPasses);
1561 CreateTooltip(hNBPasses, lmprintf(MSG_153 + ((sel >= 2) ? 3 : sel),
1562 pattern[sel][0], pattern[sel][1], pattern[sel][2], pattern[sel][3]), -1);
1563 }
1564
1565 void SetBootTypeDropdownWidth(void)
1566 {
1567 HDC hDC;
1568 HFONT hFont;
1569 SIZE sz;
1570 RECT rc;
1571
1572 if (image_path == NULL)
1573 return;
1574 // Set the maximum width of the dropdown according to the image selected
1575 GetWindowRect(hBootType, &rc);
1576 MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
1577 hDC = GetDC(hBootType);
1578 hFont = (HFONT)SendMessageA(hBootType, WM_GETFONT, 0, 0);
1579 SelectObject(hDC, hFont);
1580 GetTextExtentPointU(hDC, short_image_path, &sz);
1581 safe_release_dc(hBootType, hDC);
1582 SendMessage(hBootType, CB_SETDROPPEDWIDTH, (WPARAM)max(sz.cx + 10, rc.right - rc.left), (LPARAM)0);
1583 }
1584
1585 // Create the horizontal section lines
1586 void OnPaint(HDC hdc)
1587 {
1588 int i;
1589 HPEN hp = CreatePen(0, (fScale < 1.5f) ? 2 : 3, RGB(0, 0, 0));
1590 SelectObject(hdc, hp);
1591 for (i = 0; i < ARRAYSIZE(section_vpos); i++) {
1592 MoveToEx(hdc, mw + 10, section_vpos[i], NULL);
1593 LineTo(hdc, mw + fw, section_vpos[i]);
1594 }
1595 }