"Fossies" - the Fresh Open Source Software Archive

Member "rufus-3.13/src/ui.c" (20 Nov 2020, 60036 Bytes) of package /linux/misc/rufus-3.13.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ui.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.12_vs_3.13.

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