rufus/src/ui.c

1595 lines
58 KiB
C
Raw Permalink Normal View History

/*
* Rufus: The Reliable USB Formatting Utility
* UI-related function calls
* Copyright © 2018-2024 Pete Batard <pete@akeo.ie>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
#ifdef _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <oleacc.h>
#include <winioctl.h>
#include <assert.h>
#include "rufus.h"
#include "drive.h"
#include "missing.h"
#include "resource.h"
#include "msapi_utf8.h"
#include "localization.h"
#include "ui.h"
#include "ui_data.h"
UINT_PTR UM_LANGUAGE_MENU_MAX = UM_LANGUAGE_MENU;
HIMAGELIST hUpImageList, hDownImageList;
extern BOOL use_vds, appstore_version;
extern int imop_win_sel;
extern char *unattend_xml_path, *archive_path;
int update_progress_type = UPT_PERCENT;
int advanced_device_section_height, advanced_format_section_height;
// (empty) check box width, (empty) drop down width, button height (for and without dropdown match)
int cbw, ddw, ddbh = 0, bh = 0;
// Row Height, DropDown Height, Main button width, half dropdown width, full dropdown width
static int rh, ddh, bw, hw, fw;
// See GetFullWidth() for details on how these values are used
static int sw, mw, bsw, sbw, ssw, tw, dbw;
static WNDPROC progress_original_proc = NULL;
static wchar_t wtbtext[2][128];
static IAccPropServices* pfaps = NULL;
/*
* The following is used to allocate slots within the progress bar
* 0 means unused (no operation or no progress allocated to it)
* +n means allocate exactly n bars (n percent of the progress bar)
* -n means allocate a weighted slot of n from all remaining
* bars. E.g. if 80 slots remain and the sum of all negative entries
* is 10, -4 will allocate 4/10*80 = 32 bars (32%) for OP progress
*/
static int nb_slots[OP_MAX];
static float slot_end[OP_MAX+1]; // shifted +1 so that we can subtract 1 to OP indexes
static float previous_end;
void SetAccessibleName(HWND hCtrl, const char* name)
{
const MSAAPROPID props[] = { Name_Property_GUID };
wchar_t* wname = utf8_to_wchar(name);
SetWindowTextW(hCtrl, wname);
if (pfaps == NULL)
IGNORE_RETVAL(CoCreateInstance(&CLSID_AccPropServices, NULL, CLSCTX_INPROC, &IID_IAccPropServices, (LPVOID)&pfaps));
if (pfaps != NULL) {
IAccPropServices_ClearHwndProps(pfaps, hCtrl, OBJID_CLIENT, CHILDID_SELF, props, ARRAYSIZE(props));
IAccPropServices_SetHwndPropStr(pfaps, hCtrl, OBJID_CLIENT, CHILDID_SELF, Name_Property_GUID, wname);
}
free(wname);
}
// Set the combo selection according to the data
void SetComboEntry(HWND hDlg, int data)
{
int i, nb_entries = ComboBox_GetCount(hDlg);
if (nb_entries <= 0)
return;
for (i = 0; i < nb_entries; i++) {
if (ComboBox_GetItemData(hDlg, i) == data) {
IGNORE_RETVAL(ComboBox_SetCurSel(hDlg, i));
return;
}
}
if (i == nb_entries)
IGNORE_RETVAL(ComboBox_SetCurSel(hDlg, 0));
}
// Move a control along the Y axis
static __inline void MoveCtrlY(HWND hDlg, int nID, int vertical_shift) {
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, nID), 0, vertical_shift, 0, 0, 1.0f);
}
// https://stackoverflow.com/a/20926332/1069307
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818.aspx
void GetBasicControlsWidth(HWND hDlg)
{
int checkbox_internal_spacing = 12, dropdown_internal_spacing = 15;
RECT rc = { 0, 0, 4, 8 };
SIZE sz;
// Compute base unit sizes since GetDialogBaseUnits() returns garbage data.
// See http://support.microsoft.com/kb/125681
MapDialogRect(hDlg, &rc);
sz.cx = rc.right;
sz.cy = rc.bottom;
// TODO: figure out the specifics of each Windows version
if (WindowsVersion.Version >= WINDOWS_10) {
checkbox_internal_spacing = 10;
dropdown_internal_spacing = 13;
}
// Checkbox and (blank) dropdown widths
cbw = MulDiv(checkbox_internal_spacing, sz.cx, 4);
ddw = MulDiv(dropdown_internal_spacing, sz.cx, 4);
// Spacing width between half-length dropdowns (sep) as well as left margin
GetWindowRect(GetDlgItem(hDlg, IDC_TARGET_SYSTEM), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
sw = rc.left;
GetWindowRect(GetDlgItem(hDlg, IDC_PARTITION_TYPE), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
sw -= rc.right;
mw = rc.left;
// Small button width
SendMessage(hSaveToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
sbw = sz.cx;
// Small separator widths and button height
GetWindowRect(GetDlgItem(hDlg, IDC_SAVE), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
bh = rc.bottom - rc.top;
ssw = rc.left;
GetWindowRect(hDeviceList, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
ssw -= rc.right;
// CSM tooltip separator width
GetWindowRect(GetDlgItem(hDlg, IDS_CSM_HELP_TXT), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
tw = rc.left;
GetWindowRect(hTargetSystem, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
tw -= rc.right;
}
// Compute the minimum size of the main buttons
void GetMainButtonsWidth(HWND hDlg)
{
unsigned int i;
RECT rc;
[net] add Windows retail ISO downloads * This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED* PowerShell script, that is downloaded from GitHub and that resides in memory for the duration of a session. * The reason we use a downloaded PS script, rather than an embedded on, is because: - Microsoft have regularly been changing the deal with regards to how retail ISOs can be downloaded, and not for the better, so we can't simply embed a static means of downloading ISOs and expect that to work forever. - By using an external script, we can immediately respond to whatever new means of *ANNOYING* their legitimate users Microsoft will come up with next, as well as make sure that, the minute a new retail version of Windows becomes available, it also becomes available for download in Rufus. * Note that if you are concerned about downloading a remote PS script that is being run at the same level as an elevated application, you should understand that: - Only scripts downloaded from GitHub, from an account that is protected with 2FA, are allowed to run (i.e. someone would first have to steal a *physical* 2FA key to be in a position to upload a malicious script). - On top of this, only scripts that are signed with a separate private key (RSA + AES-256), that is itself also protected with a strong unique password which only a single person knows (and must manually enter each time they want to make a new version of the script available for download), are allowed to run. The above means that there's about as much chance for someone to manage to upload a malicious script on the GitHub servers, that Rufus would allow to run, as there is for someone to upload a malicious version of Rufus itself. Still, if you are paranoid and have concerns that, even as you can validate from its source that Rufus does not attempt to execute any remote script unless a user actively selected and clicked the DOWNLOAD button, you can also completely disable the remote script download feature, if you just set the update check to disabled (which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to enable or not, the very first time you run the application). * Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
char download[64];
GetWindowRect(GetDlgItem(hDlg, main_button_ids[0]), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
bw = rc.right - rc.left;
for (i = 0; i < ARRAYSIZE(main_button_ids); i++) {
// Make sure we add extra space for the SELECT split button (i == 0) if Fido is enabled
bw = max(bw, GetTextWidth(hDlg, main_button_ids[i]) + ((i == 0) ? (3 * cbw) / 2 : cbw));
}
[net] add Windows retail ISO downloads * This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED* PowerShell script, that is downloaded from GitHub and that resides in memory for the duration of a session. * The reason we use a downloaded PS script, rather than an embedded on, is because: - Microsoft have regularly been changing the deal with regards to how retail ISOs can be downloaded, and not for the better, so we can't simply embed a static means of downloading ISOs and expect that to work forever. - By using an external script, we can immediately respond to whatever new means of *ANNOYING* their legitimate users Microsoft will come up with next, as well as make sure that, the minute a new retail version of Windows becomes available, it also becomes available for download in Rufus. * Note that if you are concerned about downloading a remote PS script that is being run at the same level as an elevated application, you should understand that: - Only scripts downloaded from GitHub, from an account that is protected with 2FA, are allowed to run (i.e. someone would first have to steal a *physical* 2FA key to be in a position to upload a malicious script). - On top of this, only scripts that are signed with a separate private key (RSA + AES-256), that is itself also protected with a strong unique password which only a single person knows (and must manually enter each time they want to make a new version of the script available for download), are allowed to run. The above means that there's about as much chance for someone to manage to upload a malicious script on the GitHub servers, that Rufus would allow to run, as there is for someone to upload a malicious version of Rufus itself. Still, if you are paranoid and have concerns that, even as you can validate from its source that Rufus does not attempt to execute any remote script unless a user actively selected and clicked the DOWNLOAD button, you can also completely disable the remote script download feature, if you just set the update check to disabled (which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to enable or not, the very first time you run the application). * Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
// The 'CLOSE' button is also be used to display 'CANCEL' and we sometimes
// want to add "DOWNLOAD" into the Select split button => measure that too.
bw = max(bw, GetTextSize(GetDlgItem(hDlg, IDCANCEL), lmprintf(MSG_007)).cx + cbw);
static_strcpy(download, lmprintf(MSG_040));
CharUpperBuffU(download, sizeof(download));
bw = max(bw, GetTextSize(GetDlgItem(hDlg, IDC_SELECT), download).cx + (3 * cbw) / 2);
}
// The following goes over the data that gets populated into the half-width dropdowns
// (Partition scheme, Target System, Disk ID, File system, Cluster size, Nb passes)
// to figure out the minimum width we should allocate.
void GetHalfDropwdownWidth(HWND hDlg)
{
RECT rc;
unsigned int i, j, msg_id;
char tmp[256];
// Initialize half width to the UI's default size
GetWindowRect(GetDlgItem(hDlg, IDC_PARTITION_TYPE), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
hw = rc.right - rc.left - ddw;
// "Super Floppy Disk" is the longuest entry in the Partition Scheme dropdown
hw = max(hw, GetTextSize(GetDlgItem(hDlg, IDC_PARTITION_TYPE), (char*)sfd_name).cx);
// This is basically the same as SetClusterSizeLabels() except we're adding (Default) to each entry
for (i = 512, j = 1, msg_id = MSG_026; j<MAX_CLUSTER_SIZES; i <<= 1, j++) {
if (i > 8192) {
i /= 1024;
msg_id++;
}
safe_sprintf(tmp, 64, "%d %s", i, lmprintf(msg_id));
hw = max(hw, GetTextSize(GetDlgItem(hDlg, IDC_CLUSTER_SIZE), lmprintf(MSG_030, tmp)).cx);
}
// We don't go over file systems, because none of them will be longer than "Super Floppy Disk"
// We do however go over the BIOS vs UEFI entries, as some of these are translated
for (msg_id = MSG_031; msg_id <= MSG_033; msg_id++)
hw = max(hw, GetTextSize(GetDlgItem(hDlg, IDC_TARGET_SYSTEM), lmprintf(msg_id)).cx);
// Just in case, we also do the number of passes
for (i = 1; i <= 5; i++) {
char* msg = (i == 1) ? lmprintf(MSG_034, 1) : lmprintf(MSG_035, (i == 2) ? 2 : 4, (i == 2) ? "" : lmprintf(MSG_087, flash_type[i - 3]));
hw = max(hw, GetTextSize(GetDlgItem(hDlg, IDC_TARGET_SYSTEM), msg).cx);
}
// Finally, we must ensure that we'll have enough space for the checkbox controls
// that end up with a half dropdown
hw = max(hw, GetTextWidth(hDlg, IDC_BAD_BLOCKS) - sw);
// Add the width of a blank dropdown
hw += ddw;
}
/*
* dbw = dialog border width
* mw = margin width
* fw = full dropdown width
* hd = half dropdown width
* bsw = boot selection dropdown width
* sw = separator width
* ssw = small separator width
* bw = button width
* sbw = small button width
*
* | fw |
* | bsw | ssw | sbw | ssw | bw |
* 8 ->|<- 96 ->|<- 24 ->|<- 96 ->|<- 8
* mw | hw | sw | hw | mw
* | bw | ssw | bw |
*/
void GetFullWidth(HWND hDlg)
{
RECT rc;
int i;
// Get the dialog border width
GetWindowRect(hDlg, &rc);
dbw = rc.right - rc.left;
GetClientRect(hDlg, &rc);
dbw -= rc.right - rc.left;
// Compute the minimum size needed for the Boot Selection dropdown
GetWindowRect(GetDlgItem(hDlg, IDC_BOOT_SELECTION), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
bsw = max(rc.right - rc.left, GetTextSize(hBootType, lmprintf(MSG_279)).cx + ddw);
bsw = max(bsw, GetTextSize(hBootType, lmprintf(MSG_281, lmprintf(MSG_280))).cx + ddw);
// Initialize full width to the UI's default size
GetWindowRect(GetDlgItem(hDlg, IDS_DEVICE_TXT), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
fw = rc.right - rc.left - ddw;
// Go through the Image Options for Windows To Go
fw = max(fw, GetTextSize(hImageOption, lmprintf(MSG_117)).cx);
fw = max(fw, GetTextSize(hImageOption, lmprintf(MSG_118)).cx);
// Now deal with full length checkbox lines
for (i = 0; i<ARRAYSIZE(full_width_checkboxes); i++)
fw = max(fw, GetTextWidth(hDlg, full_width_checkboxes[i]));
// All of the above is for text only, so we need to add dd space
fw += ddw;
// Our min also needs to be longer than 2 half length dropdowns + spacer
fw = max(fw, 2 * hw + sw);
// Now that we have our minimum full width, adjust the button width if needed
// Adjust according to min full width
bw = max(bw, (fw - 2 * ssw - sw) / 4);
// Adjust according to min boot selection width
bw = max(bw, (bsw + sbw - sw) / 3);
// Adjust according to min half width
bw = max(bw, (hw / 2) - ssw);
// Now that our button width is set, we can adjust the rest
hw = max(hw, 2 * bw + ssw);
fw = max(fw, 2 * hw + sw);
bsw = max(bsw, fw - bw - 2 * ssw - sbw);
// TODO: Also pick a few choice messages from info/status
}
void PositionMainControls(HWND hDlg)
{
RECT rc;
HWND hCtrl, hPrevCtrl;
SIZE sz;
DWORD padding;
int i, x, button_fudge = 2;
// Start by resizing the whole dialog
GetWindowRect(hDlg, &rc);
// Don't forget to add the dialog border width, since we resize the whole dialog
SetWindowPos(hDlg, NULL, -1, -1, fw + 2 * mw + dbw, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
// Resize the height of the label, persistence size and progress bar to the height of standard dropdowns
hCtrl = GetDlgItem(hDlg, IDC_DEVICE);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
ddh = rc.bottom - rc.top;
ddbh = ddh + button_fudge;
bh = max(bh, ddbh);
hCtrl = GetDlgItem(hDlg, IDC_LABEL);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SetWindowPos(hCtrl, hAdvancedFormatToolbar, rc.left, rc.top, rc.right - rc.left, ddh, SWP_NOZORDER);
hCtrl = GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SetWindowPos(hCtrl, GetDlgItem(hDlg, IDC_PERSISTENCE_SLIDER), rc.left, rc.top, rc.right - rc.left, ddh, SWP_NOZORDER);
GetWindowRect(hProgress, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SetWindowPos(hProgress, hNBPasses, rc.left, rc.top, rc.right - rc.left, ddh, SWP_NOZORDER);
// Get the height of a typical row
hCtrl = GetDlgItem(hDlg, IDS_BOOT_SELECTION_TXT);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
rh = rc.top;
hCtrl = GetDlgItem(hDlg, IDS_DEVICE_TXT);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
rh -= rc.top;
// Get the height of the advanced options
hCtrl = GetDlgItem(hDlg, IDC_LIST_USB_HDD);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
advanced_device_section_height = rc.top;
hCtrl = GetDlgItem(hDlg, IDC_UEFI_MEDIA_VALIDATION);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
advanced_device_section_height = rc.bottom - advanced_device_section_height;
hCtrl = GetDlgItem(hDlg, IDC_QUICK_FORMAT);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
advanced_format_section_height = rc.top;
hCtrl = GetDlgItem(hDlg, IDC_BAD_BLOCKS);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
advanced_format_section_height = rc.bottom - advanced_format_section_height;
// Get the vertical position of the sections text
hCtrl = GetDlgItem(hDlg, IDS_DRIVE_PROPERTIES_TXT);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
sz = GetTextSize(hCtrl, NULL);
section_vpos[0] = rc.top + 2 * sz.cy / 3;
hCtrl = GetDlgItem(hDlg, IDS_FORMAT_OPTIONS_TXT);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
sz = GetTextSize(hCtrl, NULL);
section_vpos[1] = rc.top + 2 * sz.cy / 3;
hCtrl = GetDlgItem(hDlg, IDS_STATUS_TXT);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
sz = GetTextSize(hCtrl, NULL);
section_vpos[2] = rc.top + 2 * sz.cy / 3;
// Seriously, who designed this bullshit API call where you pass a SIZE
// struct but can only retrieve one of cx or cy at a time?!?
SendMessage(hMultiToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
GetWindowRect(GetDlgItem(hDlg, IDC_ABOUT), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SetWindowPos(hMultiToolbar, hProgress, rc.left, rc.top, sz.cx, ddbh, 0);
// Reposition the main buttons
for (i = 0; i < ARRAYSIZE(main_button_ids); i++) {
hCtrl = GetDlgItem(hDlg, main_button_ids[i]);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
x = mw + fw - bw;
if (i % 2 == 1)
x -= bw + ssw;
hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
SetWindowPos(hCtrl, hPrevCtrl, x, rc.top, bw, ddbh, 0);
}
// Reposition the Save button
hCtrl = GetDlgItem(hDlg, IDC_SAVE);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SendMessage(hSaveToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
SendMessage(hSaveToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(sz.cx, ddbh));
// Microsoft, how I loathe thee!!!
padding = (DWORD)SendMessage(hSaveToolbar, TB_GETPADDING, 0, 0);
sz.cx = padding & 0xFFFF;
sz.cy = padding >> 16;
SendMessage(hSaveToolbar, TB_SETPADDING, 0, MAKELPARAM(sz.cx + 3, sz.cy + 2));
SetWindowPos(hSaveToolbar, hDeviceList, mw + fw - sbw, rc.top, sbw, ddbh, 0);
// Reposition the Hash button
hCtrl = GetDlgItem(hDlg, IDC_HASH);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SendMessage(hHashToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
SendMessage(hHashToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(sz.cx, ddbh));
padding = (DWORD)SendMessage(hHashToolbar, TB_GETPADDING, 0, 0);
sz.cx = padding & 0xFFFF;
sz.cy = padding >> 16;
SendMessage(hHashToolbar, TB_SETPADDING, 0, MAKELPARAM(sz.cx + 3, sz.cy + 2));
SetWindowPos(hHashToolbar, hBootType, mw + bsw + ssw, rc.top, sbw, ddbh, 0);
// Reposition the Persistence slider and resize it to the boot selection width
hCtrl = GetDlgItem(hDlg, IDC_PERSISTENCE_SLIDER);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SetWindowPos(hCtrl, hImageOption, mw, rc.top, bsw, rc.bottom - rc.top, 0);
// Reposition the Persistence Units dropdown (no need to resize)
hCtrl = GetDlgItem(hDlg, IDC_PERSISTENCE_UNITS);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
sz.cx = fw - (rc.right - rc.left);
SetWindowPos(hCtrl, GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE), mw + sz.cx, rc.top, rc.right - rc.left, rc.bottom - rc.top, 0);
ShowWindow(hCtrl, SW_HIDE);
// Reposition and resize the Persistence Size edit
hCtrl = GetDlgItem(hDlg, IDC_PERSISTENCE_SIZE);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SetWindowPos(hCtrl, GetDlgItem(hDlg, IDC_PERSISTENCE_SLIDER), mw + bsw + ssw, rc.top, fw - bsw - ssw, rc.bottom - rc.top, 0);
EnableWindow(hCtrl, FALSE);
// Reposition the CSM help tip
hCtrl = GetDlgItem(hDlg, IDS_CSM_HELP_TXT);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SetWindowPos(hCtrl, hTargetSystem, mw + fw + tw, rc.top, sbw, rc.bottom - rc.top, 0);
if (advanced_mode_device) {
// Still need to adjust the width of the device selection dropdown
GetWindowRect(hDeviceList, &rc);
MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
SetWindowPos(hDeviceList, GetDlgItem(hDlg, IDS_DEVICE_TXT), rc.left, rc.top, fw - ssw - sbw, rc.bottom - rc.top, 0);
}
// Resize the full width controls
for (i = 0; i < ARRAYSIZE(full_width_controls); i++) {
hCtrl = GetDlgItem(hDlg, full_width_controls[i]);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top, fw, rc.bottom - rc.top, 0);
}
// Resize the half drowpdowns
for (i = 0; i < ARRAYSIZE(half_width_ids); i++) {
hCtrl = GetDlgItem(hDlg, half_width_ids[i]);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
// First 4 controls are on the left handside
// First 2 controls may overflow into separator
hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
SetWindowPos(hCtrl, hPrevCtrl, (i < 4) ? rc.left : mw + hw + sw, rc.top,
(i <2) ? hw + sw : hw, rc.bottom - rc.top, 0);
}
// Resize the boot selection dropdown
hCtrl = GetDlgItem(hDlg, IDC_BOOT_SELECTION);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top, bsw, rc.bottom - rc.top, 0);
}
static void ResizeDialogs(int shift)
{
RECT rc;
POINT point;
// Resize the main dialog
GetWindowRect(hMainDialog, &rc);
point.x = (rc.right - rc.left);
point.y = (rc.bottom - rc.top);
MoveWindow(hMainDialog, rc.left, rc.top, point.x, point.y + shift, TRUE);
// Resize the log
GetWindowRect(hLogDialog, &rc);
point.x = (rc.right - rc.left);
point.y = (rc.bottom - rc.top);
MoveWindow(hLogDialog, rc.left, rc.top, point.x, point.y + shift, TRUE);
MoveCtrlY(hLogDialog, IDC_LOG_CLEAR, shift);
MoveCtrlY(hLogDialog, IDC_LOG_SAVE, shift);
MoveCtrlY(hLogDialog, IDCANCEL, shift);
GetWindowRect(hLog, &rc);
point.x = (rc.right - rc.left);
point.y = (rc.bottom - rc.top) + shift;
SetWindowPos(hLog, NULL, 0, 0, point.x, point.y, SWP_NOZORDER);
// Don't forget to scroll the edit to the bottom after resize
Edit_Scroll(hLog, 0, Edit_GetLineCount(hLog));
}
// Thanks to Microsoft atrocious DPI handling, we must adjust for low DPI
void AdjustForLowDPI(HWND hDlg)
{
static int ddy = 4;
int i, j;
RECT rc;
HWND hCtrl, hPrevCtrl;
int dy = 0;
if (fScale >= 1.3f)
return;
for (i = 0; i < ARRAYSIZE(adjust_dpi_ids); i++) {
dy += ddy;
// "...and the other thing I really like about Microsoft's UI handling is how "
// "you never have to introduce weird hardcoded constants all over the place, "
// "just to make your UI look good...", said NO ONE ever.
if (adjust_dpi_ids[i][0] == IDC_QUICK_FORMAT)
dy += 1;
for (j = 0; j < 5; j++) {
if (adjust_dpi_ids[i][j] == 0)
break;
hCtrl = GetDlgItem(hDlg, adjust_dpi_ids[i][j]);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top + dy,
rc.right - rc.left, rc.bottom - rc.top, 0);
}
}
section_vpos[1] += 9 * ddy;
section_vpos[2] += 16 * ddy + 1;
advanced_device_section_height += 3 * ddy;
advanced_format_section_height += 3 * ddy + 1;
ResizeDialogs(dy + 2 * ddy);
InvalidateRect(hDlg, NULL, TRUE);
}
void SetSectionHeaders(HWND hDlg)
{
RECT rc;
HWND hCtrl;
SIZE sz;
HFONT hf;
wchar_t wtmp[128];
size_t wlen;
int i;
// Set the section header fonts and resize the static controls accordingly
hf = CreateFontA(-MulDiv(14, GetDeviceCaps(GetDC(hMainDialog), LOGPIXELSY), 72), 0, 0, 0,
FW_SEMIBOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, PROOF_QUALITY, 0, "Segoe UI");
for (i = 0; i < ARRAYSIZE(section_control_ids); i++) {
SendDlgItemMessageA(hDlg, section_control_ids[i], WM_SETFONT, (WPARAM)hf, TRUE);
hCtrl = GetDlgItem(hDlg, section_control_ids[i]);
memset(wtmp, 0, sizeof(wtmp));
2022-01-05 11:57:26 +00:00
GetWindowTextW(hCtrl, wtmp, ARRAYSIZE(wtmp) - 4);
wlen = wcslen(wtmp);
assert(wlen < ARRAYSIZE(wtmp) - 2);
wtmp[wlen++] = L' ';
wtmp[wlen++] = L' ';
SetWindowTextW(hCtrl, wtmp);
GetWindowRect(hCtrl, &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
sz = GetTextSize(hCtrl, NULL);
SetWindowPos(hCtrl, NULL, rc.left, rc.top, sz.cx, sz.cy, SWP_NOZORDER);
}
}
// Toggle "advanced" options
void ToggleAdvancedDeviceOptions(BOOL enable)
{
RECT rc;
SIZE sz;
TBBUTTONINFO button_info;
int i, shift = advanced_device_section_height;
if (!enable)
shift = -shift;
section_vpos[1] += shift;
section_vpos[2] += shift;
// Toggle the Hide/Show toolbar text
utf8_to_wchar_no_alloc(lmprintf((enable) ? MSG_122 : MSG_121, lmprintf(MSG_119)), wtbtext[0], ARRAYSIZE(wtbtext[0]));
button_info.cbSize = sizeof(button_info);
button_info.dwMask = TBIF_TEXT;
button_info.pszText = wtbtext[0];
SendMessage(hAdvancedDeviceToolbar, TB_SETBUTTONINFO, (WPARAM)IDC_ADVANCED_DRIVE_PROPERTIES, (LPARAM)&button_info);
SendMessage(hAdvancedDeviceToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)((enable) ? hUpImageList : hDownImageList));
GetWindowRect(hAdvancedDeviceToolbar, &rc);
MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
SendMessage(hAdvancedDeviceToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
// TB_GETIDEALSIZE may act up and report negative values
if (sz.cx < 16)
sz.cx = fw;
SetWindowPos(hAdvancedDeviceToolbar, hTargetSystem, rc.left, rc.top, sz.cx, rc.bottom - rc.top, 0);
// Move the controls up or down
for (i = 0; i<ARRAYSIZE(advanced_device_move_ids); i++)
MoveCtrlY(hMainDialog, advanced_device_move_ids[i], shift);
// Hide or show the various advanced options
for (i = 0; i<ARRAYSIZE(advanced_device_toggle_ids); i++)
ShowWindow(GetDlgItem(hMainDialog, advanced_device_toggle_ids[i]), enable ? SW_SHOW : SW_HIDE);
GetWindowRect(hDeviceList, &rc);
MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
SetWindowPos(hDeviceList, GetDlgItem(hMainDialog, IDS_DEVICE_TXT), rc.left, rc.top, enable ? fw - ssw - sbw : fw, rc.bottom - rc.top, 0);
// Resize the main dialog and log window
ResizeDialogs(shift);
// Never hurts to force Windows' hand
InvalidateRect(hMainDialog, NULL, TRUE);
}
void ToggleAdvancedFormatOptions(BOOL enable)
{
RECT rc;
SIZE sz;
TBBUTTONINFO button_info;
int i, shift = advanced_format_section_height;
if (!enable)
shift = -shift;
section_vpos[2] += shift;
// Toggle the Hide/Show toolbar text
utf8_to_wchar_no_alloc(lmprintf((enable) ? MSG_122 : MSG_121, lmprintf(MSG_120)), wtbtext[1], ARRAYSIZE(wtbtext[0]));
button_info.cbSize = sizeof(button_info);
button_info.dwMask = TBIF_TEXT;
button_info.pszText = wtbtext[1];
SendMessage(hAdvancedFormatToolbar, TB_SETBUTTONINFO, (WPARAM)IDC_ADVANCED_FORMAT_OPTIONS, (LPARAM)&button_info);
SendMessage(hAdvancedFormatToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)((enable) ? hUpImageList : hDownImageList));
GetWindowRect(hAdvancedFormatToolbar, &rc);
MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
SendMessage(hAdvancedFormatToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
if (sz.cx < 16)
sz.cx = fw;
SetWindowPos(hAdvancedFormatToolbar, hClusterSize, rc.left, rc.top, sz.cx, rc.bottom - rc.top, 0);
// Move the controls up or down
for (i = 0; i<ARRAYSIZE(advanced_format_move_ids); i++)
MoveCtrlY(hMainDialog, advanced_format_move_ids[i], shift);
// Hide or show the various advanced options
for (i = 0; i<ARRAYSIZE(advanced_format_toggle_ids); i++)
ShowWindow(GetDlgItem(hMainDialog, advanced_format_toggle_ids[i]), enable ? SW_SHOW : SW_HIDE);
// Resize the main dialog and log window
ResizeDialogs(shift);
// Never hurts to force Windows' hand
InvalidateRect(hMainDialog, NULL, TRUE);
}
// Toggle the display of persistence unit dropdown and resize the size field
void TogglePersistenceControls(BOOL display)
{
RECT rc;
HWND hSize, hUnits;
LONG_PTR style;
LONG width = fw - bsw - ssw;
hSize = GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE);
hUnits = GetDlgItem(hMainDialog, IDC_PERSISTENCE_UNITS);
style = GetWindowLongPtr(hSize, GWL_EXSTYLE);
if (display)
style |= WS_EX_RIGHT;
else
style &= ~WS_EX_RIGHT;
SetWindowLongPtr(hSize, GWL_EXSTYLE, style);
if (display) {
GetWindowRect(hUnits, &rc);
MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
width -= (rc.right - rc.left) + ssw;
}
GetWindowRect(hSize, &rc);
MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
SetWindowPos(hSize, GetDlgItem(hMainDialog, IDC_PERSISTENCE_SLIDER), mw + bsw + ssw, rc.top, width, rc.bottom - rc.top, 0);
EnableWindow(hSize, display ? TRUE : FALSE);
EnableWindow(hUnits, display ? TRUE : FALSE);
ShowWindow(hUnits, display ? SW_SHOW : SW_HIDE);
}
void SetPersistencePos(uint64_t pos)
{
char tmp[64];
if ((boot_type == BT_IMAGE) && (pos != 0)) {
TogglePersistenceControls(TRUE);
static_sprintf(tmp, "%ld", (LONG)pos);
} else {
TogglePersistenceControls(FALSE);
static_sprintf(tmp, "0 (%s)", lmprintf(MSG_124));
}
app_changed_size = TRUE;
SetWindowTextU(GetDlgItem(hMainDialog, IDC_PERSISTENCE_SIZE), tmp);
}
void SetPersistenceSize(void)
{
int i, proposed_unit_selection = 0;
LONGLONG base_unit = MB;
HWND hCtrl;
uint64_t max = 0, pos = 0;
if (ComboBox_GetCurSel(hDeviceList) >= 0) {
max = SelectedDrive.DiskSize - PERCENTAGE(PROJECTED_SIZE_RATIO, img_report.projected_size);
persistence_size = min(persistence_size, max);
pos = persistence_size;
// Reset the the Persistence Units dropdown
hCtrl = GetDlgItem(hMainDialog, IDC_PERSISTENCE_UNITS);
IGNORE_RETVAL(ComboBox_ResetContent(hCtrl));
for (i = 0; i < 3; i++) {
IGNORE_RETVAL(ComboBox_SetItemData(hCtrl, ComboBox_AddStringU(hCtrl, lmprintf(MSG_022 + i)), i));
// If we have more than 7 discrete positions, set this unit as our base
if (SelectedDrive.DiskSize > 7 * base_unit)
proposed_unit_selection = i;
base_unit *= 1024;
// Don't allow a base unit unless the drive is at least twice the size of that unit
if (SelectedDrive.DiskSize < 2 * base_unit)
break;
}
if (persistence_unit_selection < 0)
persistence_unit_selection = proposed_unit_selection;
IGNORE_RETVAL(ComboBox_SetCurSel(hCtrl, persistence_unit_selection));
if ((pos != 0) && (pos < MIN_EXT_SIZE))
pos = MIN_EXT_SIZE;
pos /= MB;
max /= MB;
for (i = 0; i < persistence_unit_selection; i++) {
pos /= 1024;
max /= 1024;
}
}
hCtrl = GetDlgItem(hMainDialog, IDC_PERSISTENCE_SLIDER);
// Wow! Unless you set *all* these redraw WPARAMs to true, the one from
// TBM_SETPOS gets completely ignored if the value is zero!
SendMessage(hCtrl, TBM_SETRANGEMIN, (WPARAM)TRUE, (LPARAM)0);
SendMessage(hCtrl, TBM_SETRANGEMAX, (WPARAM)TRUE, (LPARAM)max);
SendMessage(hCtrl, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pos);
SetPersistencePos(pos);
}
// Toggle the Image Option dropdown (Windows To Go or persistence settings)
void ToggleImageOptions(void)
{
BOOL has_wintogo, has_persistence;
uint8_t entry_image_options = image_options;
int i, shift = rh;
has_wintogo = ((boot_type == BT_IMAGE) && (image_path != NULL) && (img_report.is_iso || img_report.is_windows_img) &&
(WindowsVersion.Version >= WINDOWS_8) && (HAS_WINTOGO(img_report)));
has_persistence = ((boot_type == BT_IMAGE) && (image_path != NULL) && (img_report.is_iso) && (HAS_PERSISTENCE(img_report)));
assert(popcnt8(image_options) <= 1);
// Keep a copy of the "Image Option" text (so that we don't have to duplicate its translation in the .loc)
if (image_option_txt[0] == 0)
GetWindowTextU(GetDlgItem(hMainDialog, IDS_IMAGE_OPTION_TXT), image_option_txt, sizeof(image_option_txt));
if (((has_wintogo) && !(image_options & IMOP_WINTOGO)) ||
((!has_wintogo) && (image_options & IMOP_WINTOGO))) {
image_options ^= IMOP_WINTOGO;
if (image_options & IMOP_WINTOGO) {
SetWindowTextU(GetDlgItem(hMainDialog, IDS_IMAGE_OPTION_TXT), image_option_txt);
// Set the Windows To Go selection in the dropdown
IGNORE_RETVAL(ComboBox_SetCurSel(hImageOption, imop_win_sel));
}
}
if (((has_persistence) && !(image_options & IMOP_PERSISTENCE)) ||
((!has_persistence) && (image_options & IMOP_PERSISTENCE))) {
image_options ^= IMOP_PERSISTENCE;
if (image_options & IMOP_PERSISTENCE) {
SetWindowTextU(GetDlgItem(hMainDialog, IDS_IMAGE_OPTION_TXT), lmprintf(MSG_123));
TogglePersistenceControls(persistence_size != 0);
SetPersistenceSize();
}
}
if ( ((entry_image_options != 0) && (has_wintogo || has_persistence)) ||
((entry_image_options == 0) && !(has_wintogo || has_persistence)) )
shift = 0;
if (shift != 0) {
if (entry_image_options != 0)
shift = -shift;
section_vpos[1] += shift;
section_vpos[2] += shift;
for (i = 0; i < ARRAYSIZE(image_option_move_ids); i++)
MoveCtrlY(hMainDialog, image_option_move_ids[i], shift);
// Resize the main dialog and log window
ResizeDialogs(shift);
}
// Hide or show the boot options
for (i = 0; i < ARRAYSIZE(image_option_toggle_ids); i++) {
ShowWindow(GetDlgItem(hMainDialog, image_option_toggle_ids[i][0]),
(image_options & image_option_toggle_ids[i][1]) ? SW_SHOW : SW_HIDE);
}
// If you don't force a redraw here, all kind of bad UI artifacts happen...
InvalidateRect(hMainDialog, NULL, TRUE);
}
// We need to create the small toolbar buttons first so that we can compute their width
void CreateSmallButtons(HWND hDlg)
{
HIMAGELIST hImageList;
HICON hIconSave, hIconHash;
int icon_offset = 0, i16 = GetSystemMetrics(SM_CXSMICON);
TBBUTTON tbToolbarButtons[1];
unsigned char* buffer;
DWORD bufsize;
if (i16 >= 28)
icon_offset = 20;
else if (i16 >= 20)
icon_offset = 10;
hSaveToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
0, 0, 0, 0, hMainDialog, (HMENU)IDC_SAVE_TOOLBAR, hMainInstance, NULL);
hImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE | ILC_MIRROR, 1, 0);
buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDI_SAVE_16 + icon_offset), _RT_RCDATA, "save icon", &bufsize, FALSE);
hIconSave = CreateIconFromResourceEx(buffer, bufsize, TRUE, 0x30000, 0, 0, 0);
ImageList_AddIcon(hImageList, hIconSave);
DestroyIcon(hIconSave);
SendMessage(hSaveToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hImageList);
SendMessage(hSaveToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
memset(tbToolbarButtons, 0, sizeof(TBBUTTON));
tbToolbarButtons[0].idCommand = IDC_SAVE;
tbToolbarButtons[0].fsStyle = BTNS_AUTOSIZE;
tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
tbToolbarButtons[0].iBitmap = 0;
SendMessage(hSaveToolbar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)&tbToolbarButtons);
SetAccessibleName(hSaveToolbar, lmprintf(MSG_313));
hHashToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
0, 0, 0, 0, hMainDialog, (HMENU)IDC_HASH_TOOLBAR, hMainInstance, NULL);
hImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE | ILC_MIRROR, 1, 0);
buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(IDI_HASH_16 + icon_offset), _RT_RCDATA, "hash icon", &bufsize, FALSE);
hIconHash = CreateIconFromResourceEx(buffer, bufsize, TRUE, 0x30000, 0, 0, 0);
ImageList_AddIcon(hImageList, hIconHash);
DestroyIcon(hIconHash);
SendMessage(hHashToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hImageList);
SendMessage(hHashToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
memset(tbToolbarButtons, 0, sizeof(TBBUTTON));
tbToolbarButtons[0].idCommand = IDC_HASH;
tbToolbarButtons[0].fsStyle = BTNS_AUTOSIZE;
tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
tbToolbarButtons[0].iBitmap = 0;
SendMessage(hHashToolbar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)&tbToolbarButtons);
SetAccessibleName(hHashToolbar, lmprintf(MSG_314));
}
static INT_PTR CALLBACK ProgressCallback(HWND hCtrl, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
RECT rc, rc2;
PAINTSTRUCT ps;
SIZE size;
LONG full_right;
wchar_t winfo[128];
static BOOL marquee_mode = FALSE;
static uint32_t pos = 0, min = 0, max = 0xFFFF;
static COLORREF color = PROGRESS_BAR_NORMAL_COLOR;
switch (message) {
case PBM_SETSTATE:
switch (wParam) {
case PBST_NORMAL:
color = PROGRESS_BAR_NORMAL_COLOR;
break;
case PBST_PAUSED:
color = PROGRESS_BAR_PAUSED_COLOR;
break;
case PBST_ERROR:
color = PROGRESS_BAR_ERROR_COLOR;
break;
}
return (INT_PTR)TRUE;
case PBM_SETRANGE:
CallWindowProc(progress_original_proc, hCtrl, message, wParam, lParam);
// Don't bother sanity checking min and max: If *you* want to
// be an ass about the progress bar range, it's *your* problem.
min = (uint32_t)(lParam & 0xFFFF);
max = (uint32_t)(lParam >> 16);
return (INT_PTR)TRUE;
case PBM_SETPOS:
CallWindowProc(progress_original_proc, hCtrl, message, wParam, lParam);
pos = (WORD)wParam;
InvalidateRect(hProgress, NULL, TRUE);
return (INT_PTR)TRUE;
case PBM_SETMARQUEE:
CallWindowProc(progress_original_proc, hCtrl, message, wParam, lParam);
if ((wParam == TRUE) && (!marquee_mode)) {
marquee_mode = TRUE;
pos = min;
color = PROGRESS_BAR_NORMAL_COLOR;
SetTimer(hCtrl, TID_MARQUEE_TIMER, MARQUEE_TIMER_REFRESH, NULL);
InvalidateRect(hProgress, NULL, TRUE);
} else if ((wParam == FALSE) && (marquee_mode)) {
marquee_mode = FALSE;
KillTimer(hCtrl, TID_MARQUEE_TIMER);
pos = min;
InvalidateRect(hProgress, NULL, TRUE);
}
return (INT_PTR)TRUE;
case WM_TIMER:
if ((wParam == TID_MARQUEE_TIMER) && marquee_mode) {
pos += max((max - min) / (1000 / MARQUEE_TIMER_REFRESH), 1);
if ((pos > max) || (pos < min))
pos = min;
InvalidateRect(hProgress, NULL, TRUE);
return (INT_PTR)TRUE;
}
return (INT_PTR)FALSE;
case WM_PAINT:
hDC = BeginPaint(hCtrl, &ps);
GetClientRect(hCtrl, &rc);
rc2 = rc;
InflateRect(&rc, -1, -1);
SelectObject(hDC, GetStockObject(DC_PEN));
SelectObject(hDC, GetStockObject(NULL_BRUSH));
// TODO: Handle SetText message so we can avoid this call
GetWindowTextW(hProgress, winfo, ARRAYSIZE(winfo));
SelectObject(hDC, hInfoFont);
GetTextExtentPoint32(hDC, winfo, (int)wcslen(winfo), &size);
if (size.cx > rc.right)
size.cx = rc.right;
if (size.cy > rc.bottom)
size.cy = rc.bottom;
full_right = rc.right;
if (marquee_mode) {
// Optional first segment
if (pos + ((max - min) / 5) > max) {
rc.right = MulDiv(pos + ((max - min) / 5) - max, rc.right, max - min);
SetTextColor(hDC, PROGRESS_BAR_INVERTED_TEXT_COLOR);
SetBkColor(hDC, color);
ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
rc.left = rc.right;
rc.right = full_right;
}
// Optional second segment
if (pos > min) {
rc.right = MulDiv(pos - min, rc.right, max - min);
SetTextColor(hDC, PROGRESS_BAR_NORMAL_TEXT_COLOR);
SetBkColor(hDC, PROGRESS_BAR_BACKGROUND_COLOR);
ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
rc.left = rc.right;
rc.right = full_right;
}
// Second to last segment
rc.right = MulDiv(pos - min + ((max - min) / 5), rc.right, max - min);
SetTextColor(hDC, PROGRESS_BAR_INVERTED_TEXT_COLOR);
SetBkColor(hDC, color);
ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
} else {
// First segment
rc.right = (pos > min) ? MulDiv(pos - min, rc.right, max - min) : rc.left;
SetTextColor(hDC, PROGRESS_BAR_INVERTED_TEXT_COLOR);
SetBkColor(hDC, color);
ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
}
// Last segment
rc.left = rc.right;
rc.right = full_right;
SetTextColor(hDC, PROGRESS_BAR_NORMAL_TEXT_COLOR);
SetBkColor(hDC, PROGRESS_BAR_BACKGROUND_COLOR);
ExtTextOut(hDC, (full_right - size.cx) / 2, (rc.bottom - size.cy) / 2,
ETO_CLIPPED | ETO_OPAQUE | ETO_NUMERICSLOCAL, &rc, winfo, (int)wcslen(winfo), NULL);
// Bounding rectangle
SetDCPenColor(hDC, PROGRESS_BAR_BOX_COLOR);
Rectangle(hDC, rc2.left, rc2.top, rc2.right, rc2.bottom);
EndPaint(hCtrl, &ps);
return (INT_PTR)TRUE;
}
return CallWindowProc(progress_original_proc, hCtrl, message, wParam, lParam);
}
void CreateAdditionalControls(HWND hDlg)
{
int buttons_list[] = { IDC_LANG, IDC_ABOUT, IDC_SETTINGS, IDC_LOG };
int bitmaps_list[] = { 0, 1, 2, 3 };
HINSTANCE hDll;
HIMAGELIST hToolbarImageList;
HICON hIcon, hIconUp, hIconDown;
RECT rc;
SIZE sz;
int icon_offset = 0, i, i16, s16, size;
int toolbar_dx = -4 - ((fScale > 1.49f) ? 1 : 0) - ((fScale > 1.99f) ? 1 : 0);
TBBUTTON tbToolbarButtons[ARRAYSIZE(buttons_list) * 2 - 1];
unsigned char* buffer;
DWORD bufsize;
s16 = i16 = GetSystemMetrics(SM_CXSMICON);
if (s16 >= 54)
s16 = 64;
else if (s16 >= 40)
s16 = 48;
else if (s16 >= 28)
s16 = 32;
else if (s16 >= 20)
s16 = 24;
if (i16 >= 28)
icon_offset = 20;
else if (i16 >= 20)
icon_offset = 10;
// Fetch the up and down expand icons for the advanced options toolbar
hDll = GetLibraryHandle("ComDlg32");
hIconDown = (HICON)LoadImage(hDll, MAKEINTRESOURCE(577), IMAGE_ICON, s16, s16, LR_DEFAULTCOLOR | LR_SHARED);
hIconUp = (HICON)LoadImage(hDll, MAKEINTRESOURCE(578), IMAGE_ICON, s16, s16, LR_DEFAULTCOLOR | LR_SHARED);
// Fallback to using Shell32 if we can't locate the icons we want in ComDlg32 (Windows 8)
hDll = GetLibraryHandle("Shell32");
if (hIconUp == NULL)
hIconUp = (HICON)LoadImage(hDll, MAKEINTRESOURCE(16749), IMAGE_ICON, s16, s16, LR_DEFAULTCOLOR | LR_SHARED);
if (hIconDown == NULL)
hIconDown = (HICON)LoadImage(hDll, MAKEINTRESOURCE(16750), IMAGE_ICON, s16, s16, LR_DEFAULTCOLOR | LR_SHARED);
hUpImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE, 1, 0);
hDownImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE, 1, 0);
ImageList_AddIcon(hUpImageList, hIconUp);
ImageList_AddIcon(hDownImageList, hIconDown);
// Create the advanced options toolbars
memset(wtbtext, 0, sizeof(wtbtext));
utf8_to_wchar_no_alloc(lmprintf((advanced_mode_device) ? MSG_122 : MSG_121, lmprintf(MSG_119)), wtbtext[0], ARRAYSIZE(wtbtext[0]));
hAdvancedDeviceToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
0, 0, 0, 0, hMainDialog, (HMENU)IDC_ADVANCED_DEVICE_TOOLBAR, hMainInstance, NULL);
SendMessage(hAdvancedDeviceToolbar, CCM_SETVERSION, (WPARAM)6, 0);
2022-01-05 11:57:26 +00:00
memset(tbToolbarButtons, 0, sizeof(tbToolbarButtons));
tbToolbarButtons[0].idCommand = IDC_ADVANCED_DRIVE_PROPERTIES;
tbToolbarButtons[0].fsStyle = BTNS_SHOWTEXT | BTNS_AUTOSIZE;
tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
tbToolbarButtons[0].iString = (INT_PTR)wtbtext[0];
tbToolbarButtons[0].iBitmap = 0;
SendMessage(hAdvancedDeviceToolbar, TB_SETIMAGELIST, 0, (LPARAM)hUpImageList);
SendMessage(hAdvancedDeviceToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hAdvancedDeviceToolbar, TB_ADDBUTTONS, 1, (LPARAM)&tbToolbarButtons);
GetWindowRect(GetDlgItem(hDlg, IDC_ADVANCED_DRIVE_PROPERTIES), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SendMessage(hAdvancedDeviceToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
SetWindowPos(hAdvancedDeviceToolbar, hTargetSystem, rc.left + toolbar_dx, rc.top, sz.cx, rc.bottom - rc.top, 0);
SetAccessibleName(hAdvancedDeviceToolbar, lmprintf(MSG_119));
utf8_to_wchar_no_alloc(lmprintf((advanced_mode_format) ? MSG_122 : MSG_121, lmprintf(MSG_120)), wtbtext[1], ARRAYSIZE(wtbtext[1]));
hAdvancedFormatToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
0, 0, 0, 0, hMainDialog, (HMENU)IDC_ADVANCED_FORMAT_TOOLBAR, hMainInstance, NULL);
SendMessage(hAdvancedFormatToolbar, CCM_SETVERSION, (WPARAM)6, 0);
2022-01-05 11:57:26 +00:00
memset(tbToolbarButtons, 0, sizeof(tbToolbarButtons));
tbToolbarButtons[0].idCommand = IDC_ADVANCED_FORMAT_OPTIONS;
tbToolbarButtons[0].fsStyle = BTNS_SHOWTEXT | BTNS_AUTOSIZE;
tbToolbarButtons[0].fsState = TBSTATE_ENABLED;
tbToolbarButtons[0].iString = (INT_PTR)wtbtext[1];
tbToolbarButtons[0].iBitmap = 0;
SendMessage(hAdvancedFormatToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hUpImageList);
SendMessage(hAdvancedFormatToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hAdvancedFormatToolbar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)&tbToolbarButtons);
GetWindowRect(GetDlgItem(hDlg, IDC_ADVANCED_FORMAT_OPTIONS), &rc);
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
SendMessage(hAdvancedFormatToolbar, TB_GETIDEALSIZE, (WPARAM)FALSE, (LPARAM)&sz);
SetWindowPos(hAdvancedFormatToolbar, hClusterSize, rc.left + toolbar_dx, rc.top, sz.cx, rc.bottom - rc.top, 0);
SetAccessibleName(hAdvancedFormatToolbar, lmprintf(MSG_120));
// Create the multi toolbar
hMultiToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, TOOLBAR_STYLE,
0, 0, 0, 0, hMainDialog, (HMENU)IDC_MULTI_TOOLBAR, hMainInstance, NULL);
hToolbarImageList = ImageList_Create(i16, i16, ILC_COLOR32 | ILC_HIGHQUALITYSCALE, 8, 0);
for (i = 0; i < ARRAYSIZE(multitoolbar_icons); i++) {
buffer = GetResource(hMainInstance, MAKEINTRESOURCEA(multitoolbar_icons[i] + icon_offset),
_RT_RCDATA, "toolbar icon", &bufsize, FALSE);
hIcon = CreateIconFromResourceEx(buffer, bufsize, TRUE, 0x30000, 0, 0, 0);
// Mirror the "world" icon on RTL since we can't use an ImageList mirroring flag for that...
if (right_to_left_mode && (i == 0))
hIcon = CreateMirroredIcon(hIcon);
ImageList_AddIcon(hToolbarImageList, hIcon);
DestroyIcon(hIcon);
}
SendMessage(hMultiToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hToolbarImageList);
SendMessage(hMultiToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
memset(tbToolbarButtons, 0, sizeof(TBBUTTON) * ARRAYSIZE(tbToolbarButtons));
size = 2 * ARRAYSIZE(buttons_list) - 1;
if (appstore_version) {
// Remove the Update Settings button for the AppStore version
buttons_list[2] = buttons_list[3];
bitmaps_list[2] = bitmaps_list[3];
size -= 2;
}
for (i = 0; i < size; i++) {
if (i % 2 == 0) {
tbToolbarButtons[i].idCommand = buttons_list[i / 2];
tbToolbarButtons[i].fsStyle = BTNS_BUTTON;
tbToolbarButtons[i].fsState = TBSTATE_ENABLED;
tbToolbarButtons[i].iBitmap = bitmaps_list[i / 2];
} else {
tbToolbarButtons[i].fsStyle = BTNS_AUTOSIZE;
tbToolbarButtons[i].fsState = TBSTATE_INDETERMINATE;
tbToolbarButtons[i].iBitmap = I_IMAGENONE;
tbToolbarButtons[i].iString = (fScale < 1.5f) ? (INT_PTR)L"" : (INT_PTR)L" ";
}
}
SendMessage(hMultiToolbar, TB_ADDBUTTONS, (WPARAM)i, (LPARAM)&tbToolbarButtons);
SendMessage(hMultiToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(i16, ddbh));
SetAccessibleName(hMultiToolbar, lmprintf(MSG_315));
// Subclass the progress bar so that we can write on it
progress_original_proc = (WNDPROC)SetWindowLongPtr(hProgress, GWLP_WNDPROC, (LONG_PTR)ProgressCallback);
}
// Set up progress bar real estate allocation
void InitProgress(BOOL bOnlyFormat)
{
int i;
float last_end = 0.0f, slots_discrete = 0.0f, slots_analog = 0.0f;
memset(nb_slots, 0, sizeof(nb_slots));
memset(slot_end, 0, sizeof(slot_end));
previous_end = 0.0f;
if (bOnlyFormat) {
nb_slots[OP_FORMAT] = -1;
} else {
nb_slots[OP_ANALYZE_MBR] = 1;
if (IsChecked(IDC_BAD_BLOCKS)) {
nb_slots[OP_BADBLOCKS] = -1;
}
if (boot_type != BT_NON_BOOTABLE) {
// 1 extra slot for PBR writing
switch (selection_default) {
case BT_MSDOS:
nb_slots[OP_FILE_COPY] = 3 + 1;
break;
case BT_FREEDOS:
nb_slots[OP_FILE_COPY] = 5 + 1;
break;
case BT_IMAGE:
nb_slots[OP_FILE_COPY] = (img_report.is_iso || img_report.is_windows_img) ? -1 : 0;
if (HAS_WINDOWS(img_report) && (unattend_xml_path != NULL) &&
(ComboBox_GetCurItemData(hImageOption) != IMOP_WIN_TO_GO))
nb_slots[OP_PATCH] = -1;
break;
default:
nb_slots[OP_FILE_COPY] = 2 + 1;
break;
}
}
if (selection_default == BT_IMAGE && !(img_report.is_iso || img_report.is_windows_img)) {
nb_slots[OP_FORMAT] = -1;
} else {
nb_slots[OP_ZERO_MBR] = 1;
nb_slots[OP_PARTITION] = 1;
nb_slots[OP_FIX_MBR] = 1;
nb_slots[OP_CREATE_FS] = (use_vds) ? 2 :
nb_steps[ComboBox_GetCurItemData(hFileSystem)];
// So, yeah, if you're doing slow format, or using Large FAT32, and have persistence, you'll see
// the progress bar revert during format on account that we reuse the same operation for both
// partitions. Maybe one day I'll be bothered to handle two separate OP_FORMAT ops...
if ((!IsChecked(IDC_QUICK_FORMAT)) || (persistence_size != 0) || IS_EXT(fs_type) ||
((fs_type == FS_FAT32) && ((SelectedDrive.DiskSize >= LARGE_FAT32_SIZE) || (force_large_fat32)))) {
nb_slots[OP_FORMAT] = -1;
nb_slots[OP_CREATE_FS] = 0;
}
nb_slots[OP_FINALIZE] = ((selection_default == BT_IMAGE) && (fs_type == FS_NTFS)) ? 3 : 2;
}
}
if (archive_path != NULL) {
nb_slots[OP_EXTRACT_ZIP] = -1;
}
for (i = 0; i < OP_MAX; i++) {
if (nb_slots[i] > 0) {
slots_discrete += nb_slots[i] * 1.0f;
}
if (nb_slots[i] < 0) {
slots_analog += nb_slots[i] * 1.0f;
}
}
for (i = 0; i < OP_MAX; i++) {
if (nb_slots[i] == 0) {
slot_end[i + 1] = last_end;
} else if (nb_slots[i] > 0) {
slot_end[i + 1] = last_end + (1.0f * nb_slots[i]);
} else if (nb_slots[i] < 0) {
slot_end[i + 1] = last_end + (((100.0f - slots_discrete) * nb_slots[i]) / slots_analog);
}
last_end = slot_end[i + 1];
}
// If there's no analog, adjust our discrete ends to fill the whole bar
if (slots_analog == 0.0f) {
for (i = 0; i < OP_MAX; i++) {
slot_end[i + 1] *= 100.0f / slots_discrete;
}
}
}
// Position the progress bar within each operation range
void UpdateProgress(int op, float percent)
{
int pos;
static uint64_t LastRefresh = 0;
if ((op < 0) || (op >= OP_MAX)) {
duprintf("UpdateProgress: invalid op %d\n", op);
return;
}
if (percent > 100.1f) {
// duprintf("UpdateProgress(%d): invalid percentage %0.2f\n", op, percent);
return;
}
if ((percent < 0.0f) && (nb_slots[op] <= 0)) {
duprintf("UpdateProgress(%d): error negative percentage sent for negative slot value\n", op);
return;
}
if (nb_slots[op] == 0)
return;
if (previous_end < slot_end[op]) {
previous_end = slot_end[op];
}
if (percent < 0.0f) {
// Negative means advance one slot (1.0%) - requires a positive slot allocation
previous_end += (slot_end[op + 1] - slot_end[op]) / (1.0f * nb_slots[op]);
pos = (int)(previous_end / 100.0f * MAX_PROGRESS);
} else {
pos = (int)((previous_end + ((slot_end[op + 1] - previous_end) * (percent / 100.0f))) / 100.0f * MAX_PROGRESS);
}
if (pos > MAX_PROGRESS) {
duprintf("UpdateProgress(%d): rounding error - pos %d is greater than %d", op, pos, MAX_PROGRESS);
pos = MAX_PROGRESS;
}
// Reduce the refresh rate, to avoid weird effects on the sliding part of progress bar
if (GetTickCount64() > LastRefresh + (2 * MAX_REFRESH)) {
LastRefresh = GetTickCount64();
SendMessage(hProgress, PBM_SETPOS, (WPARAM)pos, 0);
SetTaskbarProgressValue(pos, MAX_PROGRESS);
}
}
/*
* The following is taken from GNU wget (progress.c)
*/
struct bar_progress {
uint64_t total_length; // expected total byte count when the download finishes
uint64_t count; // bytes downloaded so far
uint64_t last_screen_update; // time of the last screen update, measured since the beginning of download.
uint64_t dltime; // download time so far
// Keep track of recent download speeds.
struct bar_progress_hist {
uint64_t pos;
uint64_t times[SPEED_HISTORY_SIZE];
uint64_t bytes[SPEED_HISTORY_SIZE];
// The sum of times and bytes respectively, maintained for efficiency.
uint64_t total_time;
uint64_t total_bytes;
} hist;
uint64_t recent_start; // timestamp of beginning of current position.
uint64_t recent_bytes; // bytes downloaded so far.
BOOL stalled; // set when no data arrives for longer than STALL_START_TIME, then reset when new data arrives.
// The following are used to make sure that ETA information doesn't flicker.
uint64_t last_eta_time; // time of the last update to download speed and ETA, measured since the beginning of download.
int last_eta_value;
};
// This code attempts to maintain the notion of a "current" download speed, over the course
// of no less than 3s. (Shorter intervals produce very erratic results.)
//
// To do so, it samples the speed in 150ms intervals and stores the recorded samples in a
// FIFO history ring. The ring stores no more than 20 intervals, hence the history covers
// the period of at least three seconds and at most 20 reads into the past. This method
// should produce reasonable results for downloads ranging from very slow to very fast.
//
// The idea is that for fast downloads, we get the speed over exactly the last three seconds.
// For slow downloads (where a network read takes more than 150ms to complete), we get the
// speed over a larger time period, as large as it takes to complete twenty reads. This is
// good because slow downloads tend to fluctuate more and a 3-second average would be too
// erratic.
static void bar_update(struct bar_progress* bp, uint64_t howmuch, uint64_t dltime)
{
struct bar_progress_hist* hist = &bp->hist;
uint64_t recent_age = dltime - bp->recent_start;
// Update the download count.
bp->recent_bytes += howmuch;
// For very small time intervals, we return after having updated the
// "recent" download count. When its age reaches or exceeds minimum
// sample time, it will be recorded in the history ring.
if (recent_age < SPEED_SAMPLE_MIN)
return;
if (howmuch == 0) {
// If we're not downloading anything, we might be stalling,
// i.e. not downloading anything for an extended period of time.
// Since 0-reads do not enter the history ring, recent_age
// effectively measures the time since last read.
if (recent_age >= STALL_START_TIME) {
// If we're stalling, reset the ring contents because it's
// stale and because it will make bar_update stop printing
// the (bogus) current bandwidth.
bp->stalled = TRUE;
memset(hist, 0, sizeof(struct bar_progress_hist));
bp->recent_bytes = 0;
}
return;
}
// We now have a non-zero amount of to store to the speed ring.
// If the stall status was acquired, reset it.
if (bp->stalled) {
bp->stalled = FALSE;
// "recent_age" includes the entire stalled period, which
// could be very long. Don't update the speed ring with that
// value because the current bandwidth would start too small.
// Start with an arbitrary (but more reasonable) time value and
// let it level out.
recent_age = 1000;
}
// Store "recent" bytes and download time to history ring at the position POS.
// To correctly maintain the totals, first invalidate existing data
// (least recent in time) at this position. */
hist->total_time -= hist->times[hist->pos];
hist->total_bytes -= hist->bytes[hist->pos];
// Now store the new data and update the totals.
hist->times[hist->pos] = recent_age;
hist->bytes[hist->pos] = bp->recent_bytes;
hist->total_time += recent_age;
hist->total_bytes += bp->recent_bytes;
// Start a new "recent" period.
bp->recent_start = dltime;
bp->recent_bytes = 0;
// Advance the current ring position.
if (++hist->pos == SPEED_HISTORY_SIZE)
hist->pos = 0;
}
// This updates the progress bar as well as the data displayed on it so that we can
// display percentage completed, rate of transfer and estimated remaining duration.
// During init (op = OP_INIT) an optional HWND can be passed on which to look for
// a progress bar. Part of the code (eta, speed) comes from GNU wget.
void _UpdateProgressWithInfo(int op, int msg, uint64_t processed, uint64_t total, BOOL force)
{
static int last_update_progress_type = UPT_PERCENT;
static struct bar_progress bp = { 0 };
HWND hProgressDialog = (HWND)(uintptr_t)processed;
static HWND hProgressBar = NULL;
static uint64_t start_time = 0, last_refresh = 0;
uint64_t speed = 0, current_time = GetTickCount64();
double percent = 0.0;
char msg_data[128];
static BOOL bNoAltMode = FALSE;
if (op == OP_INIT) {
start_time = current_time - 1;
last_refresh = 0;
last_update_progress_type = UPT_PERCENT;
percent = 0.0f;
speed = 0;
memset(&bp, 0, sizeof(bp));
bp.total_length = total;
hProgressBar = NULL;
bNoAltMode = (BOOL)msg;
if (hProgressDialog != NULL) {
// Use the progress control provided, if any
hProgressBar = GetDlgItem(hProgressDialog, IDC_PROGRESS);
if (hProgressBar != NULL) {
SendMessage(hProgressBar, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0);
SendMessage(hProgressBar, PBM_SETMARQUEE, FALSE, 0);
SendMessage(hProgressBar, PBM_SETPOS, 0, 0);
}
SendMessage(hProgressDialog, UM_PROGRESS_INIT, 0, 0);
}
} else if ((hProgressBar != NULL) || (op > 0)) {
uint64_t dl_total_time = current_time - start_time;
uint64_t howmuch = processed - bp.count;
bp.count = processed;
bp.total_length = total;
if (bp.count > bp.total_length)
bp.total_length = bp.count;
if (bp.total_length > 0)
percent = (100.0f * bp.count) / (1.0f * bp.total_length);
else
percent = 0.0f;
if ((bp.hist.total_time > 999) && (bp.hist.total_bytes != 0)) {
// Calculate the download speed using the history ring and
// recent data that hasn't made it to the ring yet.
uint64_t dlquant = bp.hist.total_bytes + bp.recent_bytes;
uint64_t dltime = bp.hist.total_time + (dl_total_time - bp.recent_start);
speed = (dltime == 0) ? 0 : (dlquant * 1000) / dltime;
} else {
speed = 0;
}
bar_update(&bp, howmuch, dl_total_time);
if (bNoAltMode)
update_progress_type = UPT_PERCENT;
switch (update_progress_type) {
case UPT_SPEED:
if (speed != 0)
static_sprintf(msg_data, "%s/s", SizeToHumanReadable(speed, FALSE, FALSE));
else
static_sprintf(msg_data, "---");
break;
case UPT_ETA:
if ((bp.total_length > 0) && (bp.count > 0) && (dl_total_time > 3000)) {
uint32_t eta = 0;
// Don't change the value of ETA more than approximately once
// per second; doing so would cause flashing without providing
// any value to the user.
if ((bp.total_length != processed) && (bp.last_eta_value != 0) &&
(dl_total_time - bp.last_eta_time < ETA_REFRESH_INTERVAL)) {
eta = bp.last_eta_value;
} else {
// Calculate ETA using the average download speed to predict
// the future speed. If you want to use a speed averaged
// over a more recent period, replace dl_total_time with
// hist->total_time and bp->count with hist->total_bytes.
// I found that doing that results in a very jerky and
// ultimately unreliable ETA.
uint64_t bytes_remaining = bp.total_length - processed;
double d_eta = (dl_total_time / 1000.0) * (bytes_remaining * 1.0) / (bp.count * 1.0);
if (d_eta >= INT_MAX - 1)
goto skip_eta;
eta = (uint32_t)(d_eta + 0.5);
bp.last_eta_value = eta;
bp.last_eta_time = dl_total_time;
}
static_sprintf(msg_data, "%d:%02d:%02d", eta / 3600, (uint16_t)((eta % 3600) / 60), (uint16_t)(eta % 60));
} else {
skip_eta:
static_sprintf(msg_data, "-:--:--");
}
break;
default:
static_sprintf(msg_data, "%0.1f%%", percent);
break;
}
if ((force) || (bp.count == bp.total_length) || (current_time > last_refresh + MAX_REFRESH)) {
if (op < 0) {
SendMessage(hProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS * percent / 100.0f), 0);
if (op == OP_NOOP_WITH_TASKBAR)
SetTaskbarProgressValue((ULONGLONG)(MAX_PROGRESS * percent / 100.0f), MAX_PROGRESS);
} else {
UpdateProgress(op, (float)percent);
}
if ((force) || ((msg >= 0) && ((current_time > bp.last_screen_update + SCREEN_REFRESH_INTERVAL) ||
(last_update_progress_type != update_progress_type) || (bp.count == bp.total_length)))) {
PrintInfo(0, msg, msg_data);
bp.last_screen_update = current_time;
}
last_refresh = current_time;
}
last_update_progress_type = update_progress_type;
}
}
void ShowLanguageMenu(RECT rcExclude)
{
TPMPARAMS tpm;
HMENU menu;
RECT rc;
LONG nb_items = 1, adjust = 0;
loc_cmd* lcmd = NULL;
char lang[256];
char *search = "()";
char *l, *r, *str;
UM_LANGUAGE_MENU_MAX = UM_LANGUAGE_MENU;
menu = CreatePopupMenu();
list_for_each_entry(lcmd, &locale_list, loc_cmd, list) {
// The appearance of LTR languages must be fixed for RTL menus
if ((right_to_left_mode) && (!(lcmd->ctrl_id & LOC_RIGHT_TO_LEFT))) {
str = safe_strdup(lcmd->txt[1]);
l = strtok(str, search);
r = strtok(NULL, search);
static_sprintf(lang, LEFT_TO_RIGHT_EMBEDDING "(%s) " POP_DIRECTIONAL_FORMATTING "%s", r, l);
safe_free(str);
} else {
static_strcpy(lang, lcmd->txt[1]);
}
InsertMenuU(menu, -1, MF_BYPOSITION | ((selected_locale == lcmd) ? MF_CHECKED : 0), UM_LANGUAGE_MENU_MAX++, lang);
nb_items++;
}
// Empirical adjust if we have a small enough number of languages to select
if (nb_items < 20) {
GetWindowRect(hMultiToolbar, &rc);
MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
adjust = rc.top - (nb_items * ddh) / 2;
}
// Open the menu such that it doesn't overlap the specified rect
tpm.cbSize = sizeof(TPMPARAMS);
tpm.rcExclude = rcExclude;
TrackPopupMenuEx(menu, 0,
// In RTL languages, the menu should be placed at the bottom-right of the rect
right_to_left_mode ? rcExclude.right : rcExclude.left,
rcExclude.bottom + adjust, hMainDialog, &tpm);
DestroyMenu(menu);
}
void SetPassesTooltip(void)
{
const unsigned int pattern[BADLOCKS_PATTERN_TYPES][BADBLOCK_PATTERN_COUNT] =
{ BADBLOCK_PATTERN_ONE_PASS, BADBLOCK_PATTERN_TWO_PASSES, BADBLOCK_PATTERN_SLC,
BADCLOCK_PATTERN_MLC, BADBLOCK_PATTERN_TLC };
int sel = ComboBox_GetCurSel(hNBPasses);
CreateTooltip(hNBPasses, lmprintf(MSG_153 + ((sel >= 2) ? 3 : sel),
pattern[sel][0], pattern[sel][1], pattern[sel][2], pattern[sel][3]), -1);
}
void SetBootTypeDropdownWidth(void)
{
HDC hDC;
HFONT hFont;
SIZE sz;
RECT rc;
if (image_path == NULL)
return;
// Set the maximum width of the dropdown according to the image selected
GetWindowRect(hBootType, &rc);
MapWindowPoints(NULL, hMainDialog, (POINT*)&rc, 2);
hDC = GetDC(hBootType);
hFont = (HFONT)SendMessageA(hBootType, WM_GETFONT, 0, 0);
SelectObject(hDC, hFont);
GetTextExtentPointU(hDC, short_image_path, &sz);
safe_release_dc(hBootType, hDC);
SendMessage(hBootType, CB_SETDROPPEDWIDTH, (WPARAM)max(sz.cx + 10, rc.right - rc.left), (LPARAM)0);
}
// Create the horizontal section lines
void OnPaint(HDC hdc)
{
int i;
HPEN hp = CreatePen(0, (fScale < 1.5f) ? 2 : 3, RGB(0, 0, 0));
SelectObject(hdc, hp);
for (i = 0; i < ARRAYSIZE(section_vpos); i++) {
MoveToEx(hdc, mw + 10, section_vpos[i], NULL);
LineTo(hdc, mw + fw, section_vpos[i]);
}
}