mirror of
https://github.com/pbatard/rufus.git
synced 2024-08-14 23:57:05 +00:00
* Also add Ctrl-A as a new cheat-mode to toggle the use of Rufus MBR (which is enabled by default) which replaces the previous UI checkbox. The Disk ID field is now completely removed as we now use the default values for XP and non XP installs, and will expect people with multiple disks to disconnect all except the one where they plan to install Windows.
1594 lines
58 KiB
C
1594 lines
58 KiB
C
/*
|
|
* 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;
|
|
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));
|
|
}
|
|
// 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));
|
|
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);
|
|
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);
|
|
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]);
|
|
}
|
|
}
|