mirror of
https://github.com/pbatard/rufus.git
synced 2025-05-21 10:25:12 -04:00

* This commit effectively fixes https://www.kb.cert.org/vuls/id/403768 (CVE-2017-13083) as it is described per its revision 11, which is the latest revision at the time of this commit, by disabling Windows prompts, enacted during signature validation, that allow the user to bypass the intended signature verification checks. * It needs to be pointed out that the vulnerability ("allow(ing) the use of a self-signed certificate"), which relies on the end-user actively ignoring a Windows prompt that tells them that the update failed the signature validation whilst also advising against running it, is being fully addressed, even as the update protocol remains HTTP. * It also need to be pointed out that the extended delay (48 hours) between the time the vulnerability was reported and the moment it is fixed in our codebase has to do with the fact that the reporter chose to deviate from standard security practices by not disclosing the details of the vulnerability with us, be it publicly or privately, before creating the cert.org report. The only advance notification we received was a generic note about the use of HTTP vs HTTPS, which, as have established, is not immediately relevant to addressing the reported vulnerability. * Closes #1009 * Note: The other vulnerability scenario described towards the end of #1009, which doesn't have to do with the "lack of CA checking", will be addressed separately.
2137 lines
70 KiB
C
2137 lines
70 KiB
C
/*
|
|
* Rufus: The Reliable USB Formatting Utility
|
|
* Standard Dialog Routines (Browse for folder, About, etc)
|
|
* Copyright © 2011-2017 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 <shlobj.h>
|
|
#include <commdlg.h>
|
|
#include <richedit.h>
|
|
|
|
#include "rufus.h"
|
|
#include "missing.h"
|
|
#include "resource.h"
|
|
#include "msapi_utf8.h"
|
|
#include "localization.h"
|
|
|
|
#include "registry.h"
|
|
#include "settings.h"
|
|
#include "license.h"
|
|
|
|
PF_TYPE_DECL(WINAPI, HRESULT, SHCreateItemFromParsingName, (PCWSTR, IBindCtx*, REFIID, void **));
|
|
PF_TYPE_DECL(WINAPI, LPITEMIDLIST, SHSimpleIDListFromPath, (PCWSTR pszPath));
|
|
#define INIT_VISTA_SHELL32 PF_INIT(SHCreateItemFromParsingName, Shell32)
|
|
#define INIT_XP_SHELL32 PF_INIT(SHSimpleIDListFromPath, Shell32)
|
|
#define IS_VISTA_SHELL32_AVAILABLE (pfSHCreateItemFromParsingName != NULL)
|
|
|
|
/* Globals */
|
|
static HICON hMessageIcon = (HICON)INVALID_HANDLE_VALUE;
|
|
static char* szMessageText = NULL;
|
|
static char* szMessageTitle = NULL;
|
|
static char **szDialogItem;
|
|
static int nDialogItems;
|
|
static HWND hBrowseEdit;
|
|
extern HWND hUpdatesDlg;
|
|
static WNDPROC pOrgBrowseWndproc;
|
|
static const SETTEXTEX friggin_microsoft_unicode_amateurs = {ST_DEFAULT, CP_UTF8};
|
|
static BOOL notification_is_question;
|
|
static const notification_info* notification_more_info;
|
|
static BOOL settings_commcheck = FALSE;
|
|
static WNDPROC update_original_proc = NULL;
|
|
static HWINEVENTHOOK fp_weh = NULL;
|
|
static char *fp_title_str = "Microsoft Windows", *fp_button_str = "Format disk";
|
|
|
|
extern loc_cmd* selected_locale;
|
|
|
|
/*
|
|
* https://blogs.msdn.microsoft.com/oldnewthing/20040802-00/?p=38283/
|
|
*/
|
|
void SetDialogFocus(HWND hDlg, HWND hCtrl)
|
|
{
|
|
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hCtrl, TRUE);
|
|
}
|
|
|
|
/*
|
|
* We need a sub-callback to read the content of the edit box on exit and update
|
|
* our path, else if what the user typed does match the selection, it is discarded.
|
|
* Talk about a convoluted way of producing an intuitive folder selection dialog
|
|
*/
|
|
INT CALLBACK BrowseDlgCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch(message) {
|
|
case WM_DESTROY:
|
|
GetWindowTextU(hBrowseEdit, szFolderPath, sizeof(szFolderPath));
|
|
break;
|
|
}
|
|
return (INT)CallWindowProc(pOrgBrowseWndproc, hDlg, message, wParam, lParam);
|
|
}
|
|
|
|
/*
|
|
* Main BrowseInfo callback to set the initial directory and populate the edit control
|
|
*/
|
|
INT CALLBACK BrowseInfoCallback(HWND hDlg, UINT message, LPARAM lParam, LPARAM pData)
|
|
{
|
|
char dir[MAX_PATH];
|
|
wchar_t* wpath;
|
|
LPITEMIDLIST pidl;
|
|
|
|
switch(message) {
|
|
case BFFM_INITIALIZED:
|
|
pOrgBrowseWndproc = (WNDPROC)SetWindowLongPtr(hDlg, GWLP_WNDPROC, (LONG_PTR)BrowseDlgCallback);
|
|
// Windows hides the full path in the edit box by default, which is bull.
|
|
// Get a handle to the edit control to fix that
|
|
hBrowseEdit = FindWindowExA(hDlg, NULL, "Edit", NULL);
|
|
SetWindowTextU(hBrowseEdit, szFolderPath);
|
|
SetDialogFocus(hDlg, hBrowseEdit);
|
|
// On XP, BFFM_SETSELECTION can't be used with a Unicode Path in SendMessageW
|
|
// or a pidl (at least with MinGW) => must use SendMessageA
|
|
if (nWindowsVersion <= WINDOWS_XP) {
|
|
SendMessageLU(hDlg, BFFM_SETSELECTION, (WPARAM)TRUE, szFolderPath);
|
|
} else {
|
|
// On Windows 7, MinGW only properly selects the specified folder when using a pidl
|
|
wpath = utf8_to_wchar(szFolderPath);
|
|
pidl = (*pfSHSimpleIDListFromPath)(wpath);
|
|
safe_free(wpath);
|
|
// NB: see http://connect.microsoft.com/VisualStudio/feedback/details/518103/bffm-setselection-does-not-work-with-shbrowseforfolder-on-windows-7
|
|
// for details as to why we send BFFM_SETSELECTION twice.
|
|
SendMessageW(hDlg, BFFM_SETSELECTION, (WPARAM)FALSE, (LPARAM)pidl);
|
|
Sleep(100);
|
|
PostMessageW(hDlg, BFFM_SETSELECTION, (WPARAM)FALSE, (LPARAM)pidl);
|
|
}
|
|
break;
|
|
case BFFM_SELCHANGED:
|
|
// Update the status
|
|
if (SHGetPathFromIDListU((LPITEMIDLIST)lParam, dir)) {
|
|
SendMessageLU(hDlg, BFFM_SETSTATUSTEXT, 0, dir);
|
|
SetWindowTextU(hBrowseEdit, dir);
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Browse for a folder and update the folder edit box
|
|
* Will use the newer IFileOpenDialog if *compiled* for Vista or later
|
|
*/
|
|
void BrowseForFolder(void) {
|
|
|
|
BROWSEINFOW bi;
|
|
LPITEMIDLIST pidl;
|
|
WCHAR *wpath;
|
|
size_t i;
|
|
HRESULT hr;
|
|
IShellItem *psi = NULL;
|
|
IShellItem *si_path = NULL; // Automatically freed
|
|
IFileOpenDialog *pfod = NULL;
|
|
WCHAR *fname;
|
|
char* tmp_path = NULL;
|
|
|
|
dialog_showing++;
|
|
if (nWindowsVersion >= WINDOWS_VISTA) {
|
|
INIT_VISTA_SHELL32;
|
|
if (IS_VISTA_SHELL32_AVAILABLE) {
|
|
hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC,
|
|
&IID_IFileOpenDialog, (LPVOID)&pfod);
|
|
if (FAILED(hr)) {
|
|
uprintf("CoCreateInstance for FileOpenDialog failed: error %X\n", hr);
|
|
pfod = NULL; // Just in case
|
|
goto fallback;
|
|
}
|
|
hr = pfod->lpVtbl->SetOptions(pfod, FOS_PICKFOLDERS);
|
|
if (FAILED(hr)) {
|
|
uprintf("Failed to set folder option for FileOpenDialog: error %X\n", hr);
|
|
goto fallback;
|
|
}
|
|
// Set the initial folder (if the path is invalid, will simply use last)
|
|
wpath = utf8_to_wchar(szFolderPath);
|
|
// The new IFileOpenDialog makes us split the path
|
|
fname = NULL;
|
|
if ((wpath != NULL) && (wcslen(wpath) >= 1)) {
|
|
for (i = wcslen(wpath) - 1; i != 0; i--) {
|
|
if (wpath[i] == L'\\') {
|
|
wpath[i] = 0;
|
|
fname = &wpath[i + 1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = (*pfSHCreateItemFromParsingName)(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path);
|
|
if (SUCCEEDED(hr)) {
|
|
if (wpath != NULL) {
|
|
pfod->lpVtbl->SetFolder(pfod, si_path);
|
|
}
|
|
if (fname != NULL) {
|
|
pfod->lpVtbl->SetFileName(pfod, fname);
|
|
}
|
|
}
|
|
safe_free(wpath);
|
|
|
|
hr = pfod->lpVtbl->Show(pfod, hMainDialog);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = pfod->lpVtbl->GetResult(pfod, &psi);
|
|
if (SUCCEEDED(hr)) {
|
|
psi->lpVtbl->GetDisplayName(psi, SIGDN_FILESYSPATH, &wpath);
|
|
tmp_path = wchar_to_utf8(wpath);
|
|
CoTaskMemFree(wpath);
|
|
if (tmp_path == NULL) {
|
|
uprintf("Could not convert path\n");
|
|
} else {
|
|
static_strcpy(szFolderPath, tmp_path);
|
|
safe_free(tmp_path);
|
|
}
|
|
} else {
|
|
uprintf("Failed to set folder option for FileOpenDialog: error %X\n", hr);
|
|
}
|
|
} else if ((hr & 0xFFFF) != ERROR_CANCELLED) {
|
|
// If it's not a user cancel, assume the dialog didn't show and fallback
|
|
uprintf("Could not show FileOpenDialog: error %X\n", hr);
|
|
goto fallback;
|
|
}
|
|
pfod->lpVtbl->Release(pfod);
|
|
dialog_showing--;
|
|
return;
|
|
}
|
|
fallback:
|
|
if (pfod != NULL) {
|
|
pfod->lpVtbl->Release(pfod);
|
|
}
|
|
}
|
|
|
|
INIT_XP_SHELL32;
|
|
memset(&bi, 0, sizeof(BROWSEINFOW));
|
|
bi.hwndOwner = hMainDialog;
|
|
bi.lpszTitle = utf8_to_wchar(lmprintf(MSG_106));
|
|
bi.lpfn = BrowseInfoCallback;
|
|
// BIF_NONEWFOLDERBUTTON = 0x00000200 is unknown on MinGW
|
|
bi.ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS |
|
|
BIF_DONTGOBELOWDOMAIN | BIF_EDITBOX | 0x00000200;
|
|
pidl = SHBrowseForFolderW(&bi);
|
|
if (pidl != NULL) {
|
|
CoTaskMemFree(pidl);
|
|
}
|
|
safe_free(bi.lpszTitle);
|
|
dialog_showing--;
|
|
}
|
|
|
|
/*
|
|
* Return the UTF8 path of a file selected through a load or save dialog
|
|
* Will use the newer IFileOpenDialog if *compiled* for Vista or later
|
|
* All string parameters are UTF-8
|
|
* IMPORTANT NOTE: On Vista and later, remember that you need to call
|
|
* CoInitializeEx() for *EACH* thread you invoke FileDialog from, as
|
|
* GetDisplayName() will return error 0x8001010E otherwise.
|
|
*/
|
|
char* FileDialog(BOOL save, char* path, const ext_t* ext, DWORD options)
|
|
{
|
|
DWORD tmp;
|
|
OPENFILENAMEA ofn;
|
|
char selected_name[MAX_PATH];
|
|
char *ext_string = NULL, *all_files = NULL;
|
|
size_t i, j, ext_strlen;
|
|
BOOL r;
|
|
char* filepath = NULL;
|
|
HRESULT hr = FALSE;
|
|
IFileDialog *pfd = NULL;
|
|
IShellItem *psiResult;
|
|
COMDLG_FILTERSPEC* filter_spec = NULL;
|
|
wchar_t *wpath = NULL, *wfilename = NULL;
|
|
IShellItem *si_path = NULL; // Automatically freed
|
|
|
|
if ((ext == NULL) || (ext->count == 0) || (ext->extension == NULL) || (ext->description == NULL))
|
|
return NULL;
|
|
dialog_showing++;
|
|
|
|
if (nWindowsVersion >= WINDOWS_VISTA) {
|
|
INIT_VISTA_SHELL32;
|
|
filter_spec = (COMDLG_FILTERSPEC*)calloc(ext->count + 1, sizeof(COMDLG_FILTERSPEC));
|
|
if ((IS_VISTA_SHELL32_AVAILABLE) && (filter_spec != NULL)) {
|
|
// Setup the file extension filter table
|
|
for (i = 0; i < ext->count; i++) {
|
|
filter_spec[i].pszSpec = utf8_to_wchar(ext->extension[i]);
|
|
filter_spec[i].pszName = utf8_to_wchar(ext->description[i]);
|
|
}
|
|
filter_spec[i].pszSpec = L"*.*";
|
|
filter_spec[i].pszName = utf8_to_wchar(lmprintf(MSG_107));
|
|
|
|
hr = CoCreateInstance(save ? &CLSID_FileSaveDialog : &CLSID_FileOpenDialog, NULL, CLSCTX_INPROC,
|
|
&IID_IFileDialog, (LPVOID)&pfd);
|
|
|
|
if (FAILED(hr)) {
|
|
SetLastError(hr);
|
|
uprintf("CoCreateInstance for FileOpenDialog failed: %s\n", WindowsErrorString());
|
|
pfd = NULL; // Just in case
|
|
goto fallback;
|
|
}
|
|
|
|
// Set the file extension filters
|
|
pfd->lpVtbl->SetFileTypes(pfd, (UINT)ext->count + 1, filter_spec);
|
|
|
|
// Set the default directory
|
|
wpath = utf8_to_wchar(path);
|
|
hr = (*pfSHCreateItemFromParsingName)(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path);
|
|
if (SUCCEEDED(hr)) {
|
|
pfd->lpVtbl->SetFolder(pfd, si_path);
|
|
}
|
|
safe_free(wpath);
|
|
|
|
// Set the default filename
|
|
wfilename = utf8_to_wchar((ext->filename == NULL) ? "" : ext->filename);
|
|
if (wfilename != NULL) {
|
|
pfd->lpVtbl->SetFileName(pfd, wfilename);
|
|
}
|
|
|
|
// Display the dialog
|
|
hr = pfd->lpVtbl->Show(pfd, hMainDialog);
|
|
|
|
// Cleanup
|
|
safe_free(wfilename);
|
|
for (i = 0; i < ext->count; i++) {
|
|
safe_free(filter_spec[i].pszSpec);
|
|
safe_free(filter_spec[i].pszName);
|
|
}
|
|
safe_free(filter_spec[i].pszName);
|
|
safe_free(filter_spec);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
// Obtain the result of the user's interaction with the dialog.
|
|
hr = pfd->lpVtbl->GetResult(pfd, &psiResult);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = psiResult->lpVtbl->GetDisplayName(psiResult, SIGDN_FILESYSPATH, &wpath);
|
|
if (SUCCEEDED(hr)) {
|
|
filepath = wchar_to_utf8(wpath);
|
|
CoTaskMemFree(wpath);
|
|
} else {
|
|
SetLastError(hr);
|
|
uprintf("Unable to access file path: %s\n", WindowsErrorString());
|
|
}
|
|
psiResult->lpVtbl->Release(psiResult);
|
|
}
|
|
} else if ((hr & 0xFFFF) != ERROR_CANCELLED) {
|
|
// If it's not a user cancel, assume the dialog didn't show and fallback
|
|
SetLastError(hr);
|
|
uprintf("Could not show FileOpenDialog: %s\n", WindowsErrorString());
|
|
goto fallback;
|
|
}
|
|
pfd->lpVtbl->Release(pfd);
|
|
dialog_showing--;
|
|
return filepath;
|
|
}
|
|
fallback:
|
|
safe_free(filter_spec);
|
|
if (pfd != NULL) {
|
|
pfd->lpVtbl->Release(pfd);
|
|
}
|
|
}
|
|
|
|
memset(&ofn, 0, sizeof(ofn));
|
|
ofn.lStructSize = sizeof(ofn);
|
|
ofn.hwndOwner = hMainDialog;
|
|
// Selected File name
|
|
static_sprintf(selected_name, "%s", (ext->filename == NULL)?"":ext->filename);
|
|
ofn.lpstrFile = selected_name;
|
|
ofn.nMaxFile = MAX_PATH;
|
|
// Set the file extension filters
|
|
all_files = lmprintf(MSG_107);
|
|
ext_strlen = 0;
|
|
for (i=0; i<ext->count; i++) {
|
|
ext_strlen += safe_strlen(ext->description[i]) + 2*safe_strlen(ext->extension[i]) + sizeof(" ()\r\r");
|
|
}
|
|
ext_strlen += safe_strlen(all_files) + sizeof(" (*.*)\r*.*\r");
|
|
ext_string = (char*)malloc(ext_strlen+1);
|
|
if (ext_string == NULL)
|
|
return NULL;
|
|
ext_string[0] = 0;
|
|
for (i=0, j=0; i<ext->count; i++) {
|
|
j += _snprintf(&ext_string[j], ext_strlen-j, "%s (%s)\r%s\r", ext->description[i], ext->extension[i], ext->extension[i]);
|
|
}
|
|
j = _snprintf(&ext_string[j], ext_strlen-j, "%s (*.*)\r*.*\r", all_files);
|
|
// Microsoft could really have picked a better delimiter!
|
|
for (i=0; i<ext_strlen; i++) {
|
|
// Since the VS Code Analysis tool is dumb...
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(suppress: 6385)
|
|
#endif
|
|
if (ext_string[i] == '\r') {
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(suppress: 6386)
|
|
#endif
|
|
ext_string[i] = 0;
|
|
}
|
|
}
|
|
ofn.lpstrFilter = ext_string;
|
|
ofn.nFilterIndex = 1;
|
|
ofn.lpstrInitialDir = path;
|
|
ofn.Flags = OFN_OVERWRITEPROMPT | options;
|
|
// Show Dialog
|
|
if (save) {
|
|
r = GetSaveFileNameU(&ofn);
|
|
} else {
|
|
r = GetOpenFileNameU(&ofn);
|
|
}
|
|
if (r) {
|
|
filepath = safe_strdup(selected_name);
|
|
} else {
|
|
tmp = CommDlgExtendedError();
|
|
if (tmp != 0) {
|
|
uprintf("Could not select file for %s. Error %X\n", save?"save":"open", tmp);
|
|
}
|
|
}
|
|
safe_free(ext_string);
|
|
dialog_showing--;
|
|
return filepath;
|
|
}
|
|
|
|
/*
|
|
* Create the application status bar
|
|
*/
|
|
void CreateStatusBar(void)
|
|
{
|
|
SIZE sz = {0, 0};
|
|
RECT rect;
|
|
LONG x, y, width, height;
|
|
int edge[3];
|
|
TBBUTTON tbbStatusToolbarButtons[1];
|
|
TBBUTTONINFO tbi;
|
|
HFONT hFont;
|
|
HDC hDC;
|
|
|
|
// Create the status bar (WS_CLIPSIBLINGS since we have an overlapping button)
|
|
hStatus = CreateWindowExW(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_TOOLTIPS | WS_CLIPSIBLINGS,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hMainDialog,
|
|
(HMENU)IDC_STATUS, hMainInstance, NULL);
|
|
|
|
// Keep track of the status bar height
|
|
GetClientRect(hStatus, &rect);
|
|
height = rect.bottom;
|
|
|
|
// Set the font we'll use to display the '#' sign in the toolbar button
|
|
hFont = CreateFontA(-MulDiv(10, GetDeviceCaps(GetDC(hMainDialog), LOGPIXELSY), 72),
|
|
0, 0, 0, FW_MEDIUM, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
|
0, 0, PROOF_QUALITY, 0, (nWindowsVersion >= WINDOWS_VISTA)?"Segoe UI":"Arial Unicode MS");
|
|
|
|
// Find the width of our hash sign
|
|
hDC = GetDC(hMainDialog);
|
|
SelectObject(hDC, hFont);
|
|
GetTextExtentPoint32W(hDC, L"#", 1, &sz);
|
|
if (hDC != NULL)
|
|
ReleaseDC(hMainDialog, hDC);
|
|
|
|
// Create 3 status areas
|
|
GetClientRect(hMainDialog, &rect);
|
|
edge[1] = rect.right - (int)(SB_TIMER_SECTION_SIZE * fScale);
|
|
edge[0] = edge[1] - (8 + sz.cx + 8 + 1); // There's 8 absolute pixels on right and left of the text
|
|
edge[2] = rect.right;
|
|
SendMessage(hStatus, SB_SETPARTS, (WPARAM)ARRAYSIZE(edge), (LPARAM)&edge);
|
|
|
|
// NB: To add an icon on the status bar, you can use something like this:
|
|
// SendMessage(hStatus, SB_SETICON, (WPARAM) 1, (LPARAM)LoadImage(GetLibraryHandle("rasdlg"),
|
|
// MAKEINTRESOURCE(50), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR | LR_SHARED));
|
|
|
|
// This is supposed to create a toolips for a statusbar section (when SBARS_TOOLTIPS is in use)... but doesn't :(
|
|
// SendMessageLU(hStatus, SB_SETTIPTEXT, (WPARAM)2, (LPARAM)"HELLO");
|
|
|
|
// Compute the dimensions for the hash button
|
|
x = edge[0];
|
|
if (nWindowsVersion <= WINDOWS_XP) {
|
|
x -= 1;
|
|
height -= 2;
|
|
}
|
|
y = rect.bottom - height + 1;
|
|
width = edge[1] - edge[0] - 1;
|
|
// How I wish there was a way to figure out how to make Windows controls look good
|
|
// at all scales, without adding all these crappy empirical adjustments...
|
|
if ((fScale > 1.20f) && (fScale <2.40f))
|
|
height -= 1;
|
|
if (nWindowsVersion <= WINDOWS_7)
|
|
height += 1;
|
|
|
|
// Create the status toolbar
|
|
hStatusToolbar = CreateWindowExW(WS_EX_TRANSPARENT, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_TABSTOP | WS_DISABLED |
|
|
TBSTYLE_LIST | CCS_NOPARENTALIGN | CCS_NODIVIDER | CCS_NORESIZE,
|
|
x, y, width, height, hMainDialog, (HMENU)IDC_STATUS_TOOLBAR, hMainInstance, NULL);
|
|
|
|
// Set the button properties
|
|
SendMessage(hStatusToolbar, WM_SETFONT, (WPARAM)hFont, TRUE);
|
|
SendMessage(hStatusToolbar, TB_SETEXTENDEDSTYLE, 0, (LPARAM)TBSTYLE_EX_MIXEDBUTTONS);
|
|
SendMessage(hStatusToolbar, TB_SETIMAGELIST, 0, (LPARAM)NULL);
|
|
SendMessage(hStatusToolbar, TB_SETDISABLEDIMAGELIST, 0, (LPARAM)NULL);
|
|
SendMessage(hStatusToolbar, TB_SETBITMAPSIZE, 0, MAKELONG(0,0));
|
|
|
|
// Set our text
|
|
memset(tbbStatusToolbarButtons, 0, sizeof(TBBUTTON));
|
|
tbbStatusToolbarButtons[0].idCommand = IDC_HASH;
|
|
tbbStatusToolbarButtons[0].fsStyle = BTNS_SHOWTEXT;
|
|
tbbStatusToolbarButtons[0].fsState = TBSTATE_ENABLED;
|
|
tbbStatusToolbarButtons[0].iString = (INT_PTR)L"#";
|
|
SendMessage(hStatusToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
|
|
SendMessage(hStatusToolbar, TB_ADDBUTTONS, (WPARAM)1, (LPARAM)&tbbStatusToolbarButtons);
|
|
|
|
SendMessage(hStatusToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(width, height - 1));
|
|
// Yeah, you'd think that TB_SETBUTTONSIZE would work for the width... but you'd be wrong.
|
|
// The only working method that actually enforces the requested width is TB_SETBUTTONINFO
|
|
tbi.cbSize = sizeof(tbi);
|
|
tbi.dwMask = TBIF_SIZE | TBIF_COMMAND;
|
|
tbi.cx = (WORD)width;
|
|
tbi.idCommand = IDC_HASH;
|
|
SendMessage(hStatusToolbar, TB_SETBUTTONINFO, (WPARAM)IDC_HASH, (LPARAM)&tbi);
|
|
|
|
// Need to resend the positioning for the toolbar to become active... One of Windows' mysteries
|
|
// Also use this opportunity to set our Z-order for tab stop
|
|
SetWindowPos(hStatusToolbar, GetDlgItem(hMainDialog, IDCANCEL), x, y, width, height, 0);
|
|
ShowWindow(hStatusToolbar, SW_SHOWNORMAL);
|
|
}
|
|
|
|
/*
|
|
* Center a dialog with regards to the main application Window or the desktop
|
|
* See http://msdn.microsoft.com/en-us/library/windows/desktop/ms644996.aspx#init_box
|
|
*/
|
|
void CenterDialog(HWND hDlg)
|
|
{
|
|
HWND hParent;
|
|
RECT rc, rcDlg, rcParent;
|
|
|
|
if ((hParent = GetParent(hDlg)) == NULL) {
|
|
hParent = GetDesktopWindow();
|
|
}
|
|
|
|
GetWindowRect(hParent, &rcParent);
|
|
GetWindowRect(hDlg, &rcDlg);
|
|
CopyRect(&rc, &rcParent);
|
|
|
|
// Offset the parent and dialog box rectangles so that right and bottom
|
|
// values represent the width and height, and then offset the parent again
|
|
// to discard space taken up by the dialog box.
|
|
OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
|
|
OffsetRect(&rc, -rc.left, -rc.top);
|
|
OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom);
|
|
|
|
SetWindowPos(hDlg, HWND_TOP, rcParent.left + (rc.right / 2), rcParent.top + (rc.bottom / 2) - 25, 0, 0, SWP_NOSIZE);
|
|
}
|
|
|
|
// http://stackoverflow.com/questions/431470/window-border-width-and-height-in-win32-how-do-i-get-it
|
|
SIZE GetBorderSize(HWND hDlg)
|
|
{
|
|
RECT rect = {0, 0, 0, 0};
|
|
SIZE size = {0, 0};
|
|
WINDOWINFO wi;
|
|
wi.cbSize = sizeof(WINDOWINFO);
|
|
|
|
GetWindowInfo(hDlg, &wi);
|
|
|
|
AdjustWindowRectEx(&rect, wi.dwStyle, FALSE, wi.dwExStyle);
|
|
size.cx = rect.right - rect.left;
|
|
size.cy = rect.bottom - rect.top;
|
|
return size;
|
|
}
|
|
|
|
void ResizeMoveCtrl(HWND hDlg, HWND hCtrl, int dx, int dy, int dw, int dh, float scale)
|
|
{
|
|
RECT rect;
|
|
POINT point;
|
|
SIZE border;
|
|
|
|
GetWindowRect(hCtrl, &rect);
|
|
point.x = (right_to_left_mode && (hDlg != hCtrl))?rect.right:rect.left;
|
|
point.y = rect.top;
|
|
if (hDlg != hCtrl)
|
|
ScreenToClient(hDlg, &point);
|
|
GetClientRect(hCtrl, &rect);
|
|
|
|
// If the control has any borders (dialog, edit box), take them into account
|
|
border = GetBorderSize(hCtrl);
|
|
MoveWindow(hCtrl, point.x + (int)(scale*(float)dx), point.y + (int)(scale*(float)dy),
|
|
(rect.right - rect.left) + (int)(scale*(float)dw + border.cx),
|
|
(rect.bottom - rect.top) + (int)(scale*(float)dh + border.cy), TRUE);
|
|
InvalidateRect(hCtrl, NULL, TRUE);
|
|
}
|
|
|
|
/*
|
|
* License callback
|
|
*/
|
|
INT_PTR CALLBACK LicenseCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
long style;
|
|
HWND hLicense;
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
hLicense = GetDlgItem(hDlg, IDC_LICENSE_TEXT);
|
|
apply_localization(IDD_LICENSE, hDlg);
|
|
CenterDialog(hDlg);
|
|
// Suppress any inherited RTL flags
|
|
style = GetWindowLong(hLicense, GWL_EXSTYLE);
|
|
style &= ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR);
|
|
SetWindowLong(hLicense, GWL_EXSTYLE, style);
|
|
style = GetWindowLong(hLicense, GWL_STYLE);
|
|
style &= ~(ES_RIGHT);
|
|
SetWindowLong(hLicense, GWL_STYLE, style);
|
|
SetDlgItemTextA(hDlg, IDC_LICENSE_TEXT, gplv3);
|
|
break;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
reset_localization(IDD_LICENSE);
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
|
|
/*
|
|
* About dialog callback
|
|
*/
|
|
INT_PTR CALLBACK AboutCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int i, dy;
|
|
const int edit_id[2] = {IDC_ABOUT_BLURB, IDC_ABOUT_COPYRIGHTS};
|
|
char about_blurb[2048];
|
|
const char* edit_text[2] = {about_blurb, additional_copyrights};
|
|
HWND hEdit[2];
|
|
TEXTRANGEW tr;
|
|
ENLINK* enl;
|
|
RECT rect;
|
|
REQRESIZE* rsz;
|
|
wchar_t wUrl[256];
|
|
static BOOL resized_already = TRUE;
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
resized_already = FALSE;
|
|
// Execute dialog localization
|
|
apply_localization(IDD_ABOUTBOX, hDlg);
|
|
SetTitleBarIcon(hDlg);
|
|
CenterDialog(hDlg);
|
|
if (settings_commcheck)
|
|
ShowWindow(GetDlgItem(hDlg, IDC_ABOUT_UPDATES), SW_SHOW);
|
|
static_sprintf(about_blurb, about_blurb_format, lmprintf(MSG_174|MSG_RTF),
|
|
lmprintf(MSG_175|MSG_RTF, rufus_version[0], rufus_version[1], rufus_version[2]),
|
|
right_to_left_mode?"Akeo \\\\ Pete Batard 2011-2017 © Copyright":"Copyright © 2011-2017 Pete Batard / Akeo",
|
|
lmprintf(MSG_176|MSG_RTF), lmprintf(MSG_177|MSG_RTF), lmprintf(MSG_178|MSG_RTF));
|
|
for (i=0; i<ARRAYSIZE(hEdit); i++) {
|
|
hEdit[i] = GetDlgItem(hDlg, edit_id[i]);
|
|
SendMessage(hEdit[i], EM_AUTOURLDETECT, 1, 0);
|
|
/* Can't use SetDlgItemText, because it only works with RichEdit20A... and VS insists
|
|
* on reverting to RichEdit20W as soon as you edit the dialog. You can try all the W
|
|
* methods you want, it JUST WON'T WORK unless you use EM_SETTEXTEX. Also see:
|
|
* http://blog.kowalczyk.info/article/eny/Setting-unicode-rtf-text-in-rich-edit-control.html */
|
|
SendMessageA(hEdit[i], EM_SETTEXTEX, (WPARAM)&friggin_microsoft_unicode_amateurs, (LPARAM)edit_text[i]);
|
|
SendMessage(hEdit[i], EM_SETSEL, -1, -1);
|
|
SendMessage(hEdit[i], EM_SETEVENTMASK, 0, ENM_LINK|((i==0)?ENM_REQUESTRESIZE:0));
|
|
SendMessage(hEdit[i], EM_SETBKGNDCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNFACE));
|
|
}
|
|
// Need to send an explicit SetSel to avoid being positioned at the end of richedit control when tabstop is used
|
|
SendMessage(hEdit[1], EM_SETSEL, 0, 0);
|
|
SendMessage(hEdit[0], EM_REQUESTRESIZE, 0, 0);
|
|
break;
|
|
case WM_NOTIFY:
|
|
switch (((LPNMHDR)lParam)->code) {
|
|
case EN_REQUESTRESIZE:
|
|
if (!resized_already) {
|
|
resized_already = TRUE;
|
|
GetWindowRect(GetDlgItem(hDlg, edit_id[0]), &rect);
|
|
dy = rect.bottom - rect.top;
|
|
rsz = (REQRESIZE *)lParam;
|
|
dy -= rsz->rc.bottom - rsz->rc.top;
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, edit_id[0]), 0, 0, 0, -dy, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, edit_id[1]), 0, -dy, 0, dy, 1.0f);
|
|
}
|
|
break;
|
|
case EN_LINK:
|
|
enl = (ENLINK*) lParam;
|
|
if (enl->msg == WM_LBUTTONUP) {
|
|
tr.lpstrText = wUrl;
|
|
tr.chrg.cpMin = enl->chrg.cpMin;
|
|
tr.chrg.cpMax = enl->chrg.cpMax;
|
|
SendMessageW(enl->nmhdr.hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
|
|
wUrl[ARRAYSIZE(wUrl)-1] = 0;
|
|
ShellExecuteW(hDlg, L"open", wUrl, NULL, NULL, SW_SHOWNORMAL);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
reset_localization(IDD_ABOUTBOX);
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return (INT_PTR)TRUE;
|
|
case IDC_ABOUT_LICENSE:
|
|
MyDialogBox(hMainInstance, IDD_LICENSE, hDlg, LicenseCallback);
|
|
break;
|
|
case IDC_ABOUT_UPDATES:
|
|
MyDialogBox(hMainInstance, IDD_UPDATE_POLICY, hDlg, UpdateCallback);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
|
|
INT_PTR CreateAboutBox(void)
|
|
{
|
|
INT_PTR r;
|
|
dialog_showing++;
|
|
r = MyDialogBox(hMainInstance, IDD_ABOUTBOX, hMainDialog, AboutCallback);
|
|
dialog_showing--;
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* We use our own MessageBox for notifications to have greater control (center, no close button, etc)
|
|
*/
|
|
INT_PTR CALLBACK NotificationCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT loc;
|
|
int i;
|
|
// Prevent resizing
|
|
static LRESULT disabled[9] = { HTLEFT, HTRIGHT, HTTOP, HTBOTTOM, HTSIZE,
|
|
HTTOPLEFT, HTTOPRIGHT, HTBOTTOMLEFT, HTBOTTOMRIGHT };
|
|
static HBRUSH background_brush, separator_brush;
|
|
// To use the system message font
|
|
NONCLIENTMETRICS ncm;
|
|
HFONT hDlgFont;
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
if (nWindowsVersion >= WINDOWS_VISTA) { // of course, this stuff doesn't work on XP!
|
|
// Get the system message box font. See http://stackoverflow.com/a/6057761
|
|
ncm.cbSize = sizeof(ncm);
|
|
// If we're compiling with the Vista SDK or later, the NONCLIENTMETRICS struct
|
|
// will be the wrong size for previous versions, so we need to adjust it.
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
|
|
ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth);
|
|
#endif
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
|
|
hDlgFont = CreateFontIndirect(&(ncm.lfMessageFont));
|
|
// Set the dialog to use the system message box font
|
|
SendMessage(hDlg, WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDC_NOTIFICATION_TEXT), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDC_MORE_INFO), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDYES), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDNO), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
}
|
|
|
|
apply_localization(IDD_NOTIFICATION, hDlg);
|
|
background_brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
|
|
separator_brush = CreateSolidBrush(GetSysColor(COLOR_3DLIGHT));
|
|
SetTitleBarIcon(hDlg);
|
|
CenterDialog(hDlg);
|
|
// Change the default icon
|
|
if (Static_SetIcon(GetDlgItem(hDlg, IDC_NOTIFICATION_ICON), hMessageIcon) == 0) {
|
|
uprintf("Could not set dialog icon\n");
|
|
}
|
|
// Set the dialog title
|
|
if (szMessageTitle != NULL) {
|
|
SetWindowTextU(hDlg, szMessageTitle);
|
|
}
|
|
// Enable/disable the buttons and set text
|
|
if (!notification_is_question) {
|
|
SetWindowTextU(GetDlgItem(hDlg, IDNO), lmprintf(MSG_006));
|
|
} else {
|
|
ShowWindow(GetDlgItem(hDlg, IDYES), SW_SHOW);
|
|
}
|
|
if ((notification_more_info != NULL) && (notification_more_info->callback != NULL)) {
|
|
ShowWindow(GetDlgItem(hDlg, IDC_MORE_INFO), SW_SHOW);
|
|
}
|
|
// Set the control text
|
|
if (szMessageText != NULL) {
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_NOTIFICATION_TEXT), szMessageText);
|
|
}
|
|
return (INT_PTR)TRUE;
|
|
case WM_CTLCOLORSTATIC:
|
|
// Change the background colour for static text and icon
|
|
SetBkMode((HDC)wParam, TRANSPARENT);
|
|
if ((HWND)lParam == GetDlgItem(hDlg, IDC_NOTIFICATION_LINE)) {
|
|
return (INT_PTR)separator_brush;
|
|
}
|
|
return (INT_PTR)background_brush;
|
|
case WM_NCHITTEST:
|
|
// Check coordinates to prevent resize actions
|
|
loc = DefWindowProc(hDlg, message, wParam, lParam);
|
|
for(i = 0; i < 9; i++) {
|
|
if (loc == disabled[i]) {
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
case IDYES:
|
|
case IDNO:
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return (INT_PTR)TRUE;
|
|
case IDC_MORE_INFO:
|
|
if (notification_more_info != NULL)
|
|
MyDialogBox(hMainInstance, notification_more_info->id, hDlg, notification_more_info->callback);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
|
|
/*
|
|
* Display a custom notification
|
|
*/
|
|
BOOL Notification(int type, const notification_info* more_info, char* title, char* format, ...)
|
|
{
|
|
BOOL ret;
|
|
va_list args;
|
|
|
|
dialog_showing++;
|
|
szMessageText = (char*)malloc(MAX_PATH);
|
|
if (szMessageText == NULL) return FALSE;
|
|
szMessageTitle = safe_strdup(title);
|
|
if (szMessageTitle == NULL) return FALSE;
|
|
va_start(args, format);
|
|
safe_vsnprintf(szMessageText, MAX_PATH-1, format, args);
|
|
va_end(args);
|
|
szMessageText[MAX_PATH-1] = 0;
|
|
notification_more_info = more_info;
|
|
notification_is_question = FALSE;
|
|
|
|
switch(type) {
|
|
case MSG_WARNING_QUESTION:
|
|
notification_is_question = TRUE;
|
|
// Fall through
|
|
case MSG_WARNING:
|
|
hMessageIcon = LoadIcon(NULL, IDI_WARNING);
|
|
break;
|
|
case MSG_ERROR:
|
|
hMessageIcon = LoadIcon(NULL, IDI_ERROR);
|
|
break;
|
|
case MSG_QUESTION:
|
|
hMessageIcon = LoadIcon(NULL, IDI_QUESTION);
|
|
notification_is_question = TRUE;
|
|
break;
|
|
case MSG_INFO:
|
|
default:
|
|
hMessageIcon = LoadIcon(NULL, IDI_INFORMATION);
|
|
break;
|
|
}
|
|
ret = (MyDialogBox(hMainInstance, IDD_NOTIFICATION, hMainDialog, NotificationCallback) == IDYES);
|
|
safe_free(szMessageText);
|
|
safe_free(szMessageTitle);
|
|
dialog_showing--;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Custom dialog for radio button selection dialog
|
|
*/
|
|
INT_PTR CALLBACK SelectionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT loc;
|
|
int i, dh, r = -1;
|
|
// Prevent resizing
|
|
static LRESULT disabled[9] = { HTLEFT, HTRIGHT, HTTOP, HTBOTTOM, HTSIZE,
|
|
HTTOPLEFT, HTTOPRIGHT, HTBOTTOMLEFT, HTBOTTOMRIGHT };
|
|
static HBRUSH background_brush, separator_brush;
|
|
// To use the system message font
|
|
NONCLIENTMETRICS ncm;
|
|
RECT rect, rect2;
|
|
HFONT hDlgFont;
|
|
HWND hCtrl;
|
|
HDC hDC;
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
// Don't overflow our max radio button
|
|
if (nDialogItems > (IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1 + 1)) {
|
|
uprintf("Warning: Too many options requested for Selection (%d vs %d)",
|
|
nDialogItems, IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1);
|
|
nDialogItems = IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1;
|
|
}
|
|
// TODO: This shouldn't be needed when using DS_SHELLFONT
|
|
// Get the system message box font. See http://stackoverflow.com/a/6057761
|
|
ncm.cbSize = sizeof(ncm);
|
|
// If we're compiling with the Vista SDK or later, the NONCLIENTMETRICS struct
|
|
// will be the wrong size for previous versions, so we need to adjust it.
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
|
|
if (nWindowsVersion >= WINDOWS_VISTA) {
|
|
// In versions of Windows prior to Vista, the iPaddedBorderWidth member
|
|
// is not present, so we need to subtract its size from cbSize.
|
|
ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth);
|
|
}
|
|
#endif
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
|
|
hDlgFont = CreateFontIndirect(&(ncm.lfMessageFont));
|
|
// Set the dialog to use the system message box font
|
|
SendMessage(hDlg, WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDC_SELECTION_TEXT), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
for (i = 0; i < nDialogItems; i++)
|
|
SendMessage(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDYES), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDNO), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
|
|
apply_localization(IDD_SELECTION, hDlg);
|
|
background_brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
|
|
separator_brush = CreateSolidBrush(GetSysColor(COLOR_3DLIGHT));
|
|
SetTitleBarIcon(hDlg);
|
|
CenterDialog(hDlg);
|
|
// Change the default icon and set the text
|
|
Static_SetIcon(GetDlgItem(hDlg, IDC_SELECTION_ICON), LoadIcon(NULL, IDI_QUESTION));
|
|
SetWindowTextU(hDlg, szMessageTitle);
|
|
SetWindowTextU(GetDlgItem(hDlg, IDCANCEL), lmprintf(MSG_007));
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_SELECTION_TEXT), szMessageText);
|
|
for (i = 0; i < nDialogItems; i++) {
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), szDialogItem[i]);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), SW_SHOW);
|
|
}
|
|
// Move/Resize the controls as needed to fit our text
|
|
hCtrl = GetDlgItem(hDlg, IDC_SELECTION_TEXT);
|
|
hDC = GetDC(hCtrl);
|
|
SelectFont(hDC, hDlgFont); // Yes, you *MUST* reapply the font to the DC, even after SetWindowText!
|
|
GetWindowRect(hCtrl, &rect);
|
|
dh = rect.bottom - rect.top;
|
|
DrawTextU(hDC, szMessageText, -1, &rect, DT_CALCRECT | DT_WORDBREAK);
|
|
dh = rect.bottom - rect.top - dh;
|
|
if (hDC != NULL)
|
|
ReleaseDC(hCtrl, hDC);
|
|
ResizeMoveCtrl(hDlg, hCtrl, 0, 0, 0, dh, 1.0f);
|
|
for (i = 0; i < nDialogItems; i++)
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), 0, dh, 0, 0, 1.0f);
|
|
if (nDialogItems > 2) {
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_SELECTION_CHOICE2), &rect);
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + nDialogItems - 1), &rect2);
|
|
dh += rect2.top - rect.top;
|
|
}
|
|
ResizeMoveCtrl(hDlg, hDlg, 0, 0, 0, dh, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, -1), 0, 0, 0, dh, 1.0f); // IDC_STATIC = -1
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SELECTION_LINE), 0, dh, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDOK), 0, dh, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDCANCEL), 0, dh, 0, 0, 1.0f);
|
|
|
|
// Set the radio selection
|
|
Button_SetCheck(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1), BST_CHECKED);
|
|
Button_SetCheck(GetDlgItem(hDlg, IDC_SELECTION_CHOICE2), BST_UNCHECKED);
|
|
return (INT_PTR)TRUE;
|
|
case WM_CTLCOLORSTATIC:
|
|
// Change the background colour for static text and icon
|
|
SetBkMode((HDC)wParam, TRANSPARENT);
|
|
if ((HWND)lParam == GetDlgItem(hDlg, IDC_NOTIFICATION_LINE)) {
|
|
return (INT_PTR)separator_brush;
|
|
}
|
|
return (INT_PTR)background_brush;
|
|
case WM_NCHITTEST:
|
|
// Check coordinates to prevent resize actions
|
|
loc = DefWindowProc(hDlg, message, wParam, lParam);
|
|
for (i = 0; i < 9; i++) {
|
|
if (loc == disabled[i]) {
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDOK:
|
|
for (i = 0; (i < nDialogItems) &&
|
|
(Button_GetCheck(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i)) != BST_CHECKED); i++);
|
|
if (i < nDialogItems)
|
|
r = i + 1;
|
|
// Fall through
|
|
case IDNO:
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, r);
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
|
|
/*
|
|
* Display an item selection dialog
|
|
*/
|
|
int SelectionDialog(char* title, char* message, char** choices, int size)
|
|
{
|
|
int ret;
|
|
|
|
dialog_showing++;
|
|
szMessageTitle = title;
|
|
szMessageText = message;
|
|
szDialogItem = choices;
|
|
nDialogItems = size;
|
|
ret = (int)MyDialogBox(hMainInstance, IDD_SELECTION, hMainDialog, SelectionCallback);
|
|
dialog_showing--;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Custom dialog for list dialog
|
|
*/
|
|
INT_PTR CALLBACK ListCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT loc;
|
|
int i, dh, r = -1;
|
|
// Prevent resizing
|
|
static LRESULT disabled[9] = { HTLEFT, HTRIGHT, HTTOP, HTBOTTOM, HTSIZE,
|
|
HTTOPLEFT, HTTOPRIGHT, HTBOTTOMLEFT, HTBOTTOMRIGHT };
|
|
static HBRUSH background_brush, separator_brush;
|
|
// To use the system message font
|
|
NONCLIENTMETRICS ncm;
|
|
RECT rect, rect2;
|
|
HFONT hDlgFont;
|
|
HWND hCtrl;
|
|
HDC hDC;
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
// Don't overflow our max radio button
|
|
if (nDialogItems > (IDC_LIST_ITEMMAX - IDC_LIST_ITEM1 + 1)) {
|
|
uprintf("Warning: Too many items requested for List (%d vs %d)",
|
|
nDialogItems, IDC_LIST_ITEMMAX - IDC_LIST_ITEM1);
|
|
nDialogItems = IDC_LIST_ITEMMAX - IDC_LIST_ITEM1;
|
|
}
|
|
// TODO: This shouldn't be needed when using DS_SHELLFONT
|
|
// Get the system message box font. See http://stackoverflow.com/a/6057761
|
|
ncm.cbSize = sizeof(ncm);
|
|
// If we're compiling with the Vista SDK or later, the NONCLIENTMETRICS struct
|
|
// will be the wrong size for previous versions, so we need to adjust it.
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
|
|
if (nWindowsVersion >= WINDOWS_VISTA) {
|
|
// In versions of Windows prior to Vista, the iPaddedBorderWidth member
|
|
// is not present, so we need to subtract its size from cbSize.
|
|
ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth);
|
|
}
|
|
#endif
|
|
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
|
|
hDlgFont = CreateFontIndirect(&(ncm.lfMessageFont));
|
|
// Set the dialog to use the system message box font
|
|
SendMessage(hDlg, WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDC_LIST_TEXT), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
for (i = 0; i < nDialogItems; i++)
|
|
SendMessage(GetDlgItem(hDlg, IDC_LIST_ITEM1 + i), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDYES), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
SendMessage(GetDlgItem(hDlg, IDNO), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
|
|
|
apply_localization(IDD_LIST, hDlg);
|
|
background_brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
|
|
separator_brush = CreateSolidBrush(GetSysColor(COLOR_3DLIGHT));
|
|
SetTitleBarIcon(hDlg);
|
|
CenterDialog(hDlg);
|
|
// Change the default icon and set the text
|
|
Static_SetIcon(GetDlgItem(hDlg, IDC_LIST_ICON), LoadIcon(NULL, IDI_EXCLAMATION));
|
|
SetWindowTextU(hDlg, szMessageTitle);
|
|
SetWindowTextU(GetDlgItem(hDlg, IDCANCEL), lmprintf(MSG_007));
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_LIST_TEXT), szMessageText);
|
|
for (i = 0; i < nDialogItems; i++) {
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_LIST_ITEM1 + i), szDialogItem[i]);
|
|
ShowWindow(GetDlgItem(hDlg, IDC_LIST_ITEM1 + i), SW_SHOW);
|
|
}
|
|
// Move/Resize the controls as needed to fit our text
|
|
hCtrl = GetDlgItem(hDlg, IDC_LIST_TEXT);
|
|
hDC = GetDC(hCtrl);
|
|
SelectFont(hDC, hDlgFont); // Yes, you *MUST* reapply the font to the DC, even after SetWindowText!
|
|
GetWindowRect(hCtrl, &rect);
|
|
dh = rect.bottom - rect.top;
|
|
DrawTextU(hDC, szMessageText, -1, &rect, DT_CALCRECT | DT_WORDBREAK);
|
|
dh = rect.bottom - rect.top - dh;
|
|
if (hDC != NULL)
|
|
ReleaseDC(hCtrl, hDC);
|
|
ResizeMoveCtrl(hDlg, hCtrl, 0, 0, 0, dh, 1.0f);
|
|
for (i = 0; i < nDialogItems; i++)
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_LIST_ITEM1 + i), 0, dh, 0, 0, 1.0f);
|
|
if (nDialogItems > 1) {
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_LIST_ITEM1), &rect);
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_LIST_ITEM1 + nDialogItems - 1), &rect2);
|
|
dh += rect2.top - rect.top;
|
|
}
|
|
ResizeMoveCtrl(hDlg, hDlg, 0, 0, 0, dh, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, -1), 0, 0, 0, dh, 1.0f); // IDC_STATIC = -1
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_LIST_LINE), 0, dh, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDOK), 0, dh, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDCANCEL), 0, dh, 0, 0, 1.0f);
|
|
return (INT_PTR)TRUE;
|
|
case WM_CTLCOLORSTATIC:
|
|
// Change the background colour for static text and icon
|
|
SetBkMode((HDC)wParam, TRANSPARENT);
|
|
if ((HWND)lParam == GetDlgItem(hDlg, IDC_NOTIFICATION_LINE)) {
|
|
return (INT_PTR)separator_brush;
|
|
}
|
|
return (INT_PTR)background_brush;
|
|
case WM_NCHITTEST:
|
|
// Check coordinates to prevent resize actions
|
|
loc = DefWindowProc(hDlg, message, wParam, lParam);
|
|
for (i = 0; i < 9; i++) {
|
|
if (loc == disabled[i]) {
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDOK:
|
|
case IDNO:
|
|
case IDCANCEL:
|
|
EndDialog(hDlg, r);
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
|
|
/*
|
|
* Display a dialog with a list of items
|
|
*/
|
|
void ListDialog(char* title, char* message, char** items, int size)
|
|
{
|
|
dialog_showing++;
|
|
szMessageTitle = title;
|
|
szMessageText = message;
|
|
szDialogItem = items;
|
|
nDialogItems = size;
|
|
MyDialogBox(hMainInstance, IDD_LIST, hMainDialog, ListCallback);
|
|
dialog_showing--;
|
|
}
|
|
|
|
static struct {
|
|
HWND hTip; // Tooltip handle
|
|
HWND hCtrl; // Handle of the control the tooltip belongs to
|
|
WNDPROC original_proc;
|
|
LPWSTR wstring;
|
|
} ttlist[MAX_TOOLTIPS] = { {0} };
|
|
|
|
INT_PTR CALLBACK TooltipCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LPNMTTDISPINFOW lpnmtdi;
|
|
int i = MAX_TOOLTIPS;
|
|
|
|
// Make sure we have an original proc
|
|
for (i=0; i<MAX_TOOLTIPS; i++) {
|
|
if (ttlist[i].hTip == hDlg) break;
|
|
}
|
|
if (i == MAX_TOOLTIPS)
|
|
return (INT_PTR)FALSE;
|
|
|
|
switch (message) {
|
|
case WM_NOTIFY:
|
|
switch (((LPNMHDR)lParam)->code) {
|
|
case TTN_GETDISPINFOW:
|
|
lpnmtdi = (LPNMTTDISPINFOW)lParam;
|
|
lpnmtdi->lpszText = ttlist[i].wstring;
|
|
SendMessage(hDlg, TTM_SETMAXTIPWIDTH, 0, 300);
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
break;
|
|
}
|
|
#ifdef _DEBUG
|
|
// comctl32 causes issues if the tooltips are not being manipulated from the same thread as their parent controls
|
|
if (GetCurrentThreadId() != MainThreadId)
|
|
uprintf("Warning: Tooltip callback is being called from wrong thread");
|
|
#endif
|
|
return CallWindowProc(ttlist[i].original_proc, hDlg, message, wParam, lParam);
|
|
}
|
|
|
|
/*
|
|
* Create a tooltip for the control passed as first parameter
|
|
* duration sets the duration in ms. Use -1 for default
|
|
* message is an UTF-8 string
|
|
*/
|
|
BOOL CreateTooltip(HWND hControl, const char* message, int duration)
|
|
{
|
|
TOOLINFOW toolInfo = {0};
|
|
int i;
|
|
|
|
if ( (hControl == NULL) || (message == NULL) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Destroy existing tooltip if any
|
|
DestroyTooltip(hControl);
|
|
|
|
// Find an empty slot
|
|
for (i=0; i<MAX_TOOLTIPS; i++) {
|
|
if (ttlist[i].hTip == NULL) break;
|
|
}
|
|
if (i >= MAX_TOOLTIPS) {
|
|
uprintf("Maximum number of tooltips reached (%d)\n", MAX_TOOLTIPS);
|
|
return FALSE;
|
|
}
|
|
|
|
// Create the tooltip window
|
|
ttlist[i].hTip = CreateWindowExW(0, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hMainDialog, NULL,
|
|
hMainInstance, NULL);
|
|
|
|
if (ttlist[i].hTip == NULL) {
|
|
return FALSE;
|
|
}
|
|
ttlist[i].hCtrl = hControl;
|
|
|
|
// Subclass the tooltip to handle multiline
|
|
ttlist[i].original_proc = (WNDPROC)SetWindowLongPtr(ttlist[i].hTip, GWLP_WNDPROC, (LONG_PTR)TooltipCallback);
|
|
|
|
// Set the string to display (can be multiline)
|
|
ttlist[i].wstring = utf8_to_wchar(message);
|
|
|
|
// Set tooltip duration (ms)
|
|
PostMessage(ttlist[i].hTip, TTM_SETDELAYTIME, (WPARAM)TTDT_AUTOPOP, (LPARAM)duration);
|
|
|
|
// Associate the tooltip to the control
|
|
toolInfo.cbSize = sizeof(toolInfo);
|
|
toolInfo.hwnd = ttlist[i].hTip; // Set to the tooltip itself to ease up subclassing
|
|
toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS | ((right_to_left_mode)?TTF_RTLREADING:0);
|
|
// set TTF_NOTBUTTON and TTF_CENTERTIP if it isn't a button
|
|
if (!(SendMessage(hControl, WM_GETDLGCODE, 0, 0) & DLGC_BUTTON))
|
|
toolInfo.uFlags |= 0x80000000L | TTF_CENTERTIP;
|
|
|
|
toolInfo.uId = (UINT_PTR)hControl;
|
|
toolInfo.lpszText = LPSTR_TEXTCALLBACKW;
|
|
SendMessageW(ttlist[i].hTip, TTM_ADDTOOLW, 0, (LPARAM)&toolInfo);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Destroy a tooltip. hCtrl = handle of the control the tooltip is associated with */
|
|
void DestroyTooltip(HWND hControl)
|
|
{
|
|
int i;
|
|
|
|
if (hControl == NULL) return;
|
|
for (i=0; i<MAX_TOOLTIPS; i++) {
|
|
if (ttlist[i].hCtrl == hControl) break;
|
|
}
|
|
if (i >= MAX_TOOLTIPS) return;
|
|
DestroyWindow(ttlist[i].hTip);
|
|
safe_free(ttlist[i].wstring);
|
|
ttlist[i].original_proc = NULL;
|
|
ttlist[i].hTip = NULL;
|
|
ttlist[i].hCtrl = NULL;
|
|
}
|
|
|
|
void DestroyAllTooltips(void)
|
|
{
|
|
int i, j;
|
|
|
|
for (i=0, j=0; i<MAX_TOOLTIPS; i++) {
|
|
if (ttlist[i].hTip == NULL) continue;
|
|
j++;
|
|
DestroyWindow(ttlist[i].hTip);
|
|
safe_free(ttlist[i].wstring);
|
|
ttlist[i].original_proc = NULL;
|
|
ttlist[i].hTip = NULL;
|
|
ttlist[i].hCtrl = NULL;
|
|
}
|
|
}
|
|
|
|
/* Determine if a Windows is being displayed or not */
|
|
BOOL IsShown(HWND hDlg)
|
|
{
|
|
WINDOWPLACEMENT placement = {0};
|
|
placement.length = sizeof(WINDOWPLACEMENT);
|
|
if (!GetWindowPlacement(hDlg, &placement))
|
|
return FALSE;
|
|
switch (placement.showCmd) {
|
|
case SW_SHOWNORMAL:
|
|
case SW_SHOWMAXIMIZED:
|
|
case SW_SHOW:
|
|
case SW_SHOWDEFAULT:
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Compute the width of a dropdown list entry */
|
|
LONG GetEntryWidth(HWND hDropDown, const char *entry)
|
|
{
|
|
HDC hDC;
|
|
HFONT hFont, hDefFont = NULL;
|
|
SIZE size;
|
|
|
|
hDC = GetDC(hDropDown);
|
|
hFont = (HFONT)SendMessage(hDropDown, WM_GETFONT, 0, 0);
|
|
if (hFont != NULL)
|
|
hDefFont = (HFONT)SelectObject(hDC, hFont);
|
|
|
|
if (!GetTextExtentPointU(hDC, entry, &size))
|
|
size.cx = 0;
|
|
|
|
if (hFont != NULL)
|
|
SelectObject(hDC, hDefFont);
|
|
|
|
if (hDC != NULL)
|
|
ReleaseDC(hDropDown, hDC);
|
|
return size.cx;
|
|
}
|
|
|
|
/*
|
|
* Windows 7 taskbar icon handling (progress bar overlay, etc)
|
|
* Some platforms don't have these, so we redefine
|
|
*/
|
|
typedef enum MY_STPFLAG
|
|
{
|
|
MY_STPF_NONE = 0,
|
|
MY_STPF_USEAPPTHUMBNAILALWAYS = 0x1,
|
|
MY_STPF_USEAPPTHUMBNAILWHENACTIVE = 0x2,
|
|
MY_STPF_USEAPPPEEKALWAYS = 0x4,
|
|
MY_STPF_USEAPPPEEKWHENACTIVE = 0x8
|
|
} MY_STPFLAG;
|
|
|
|
typedef enum MY_THUMBBUTTONMASK
|
|
{
|
|
MY_THB_BITMAP = 0x1,
|
|
MY_THB_ICON = 0x2,
|
|
MY_THB_TOOLTIP = 0x4,
|
|
MY_THB_FLAGS = 0x8
|
|
} MY_THUMBBUTTONMASK;
|
|
|
|
typedef enum MY_THUMBBUTTONFLAGS
|
|
{
|
|
MY_THBF_ENABLED = 0,
|
|
MY_THBF_DISABLED = 0x1,
|
|
MY_THBF_DISMISSONCLICK = 0x2,
|
|
MY_THBF_NOBACKGROUND = 0x4,
|
|
MY_THBF_HIDDEN = 0x8,
|
|
MY_THBF_NONINTERACTIVE = 0x10
|
|
} MY_THUMBBUTTONFLAGS;
|
|
|
|
typedef struct MY_THUMBBUTTON
|
|
{
|
|
MY_THUMBBUTTONMASK dwMask;
|
|
UINT iId;
|
|
UINT iBitmap;
|
|
HICON hIcon;
|
|
WCHAR szTip[260];
|
|
MY_THUMBBUTTONFLAGS dwFlags;
|
|
} MY_THUMBBUTTON;
|
|
|
|
/*
|
|
typedef enum MY_TBPFLAG
|
|
{
|
|
TASKBAR_NOPROGRESS = 0,
|
|
TASKBAR_INDETERMINATE = 0x1,
|
|
TASKBAR_NORMAL = 0x2,
|
|
TASKBAR_ERROR = 0x4,
|
|
TASKBAR_PAUSED = 0x8
|
|
} MY_TBPFLAG;
|
|
*/
|
|
|
|
#pragma push_macro("INTERFACE")
|
|
#undef INTERFACE
|
|
#define INTERFACE my_ITaskbarList3
|
|
DECLARE_INTERFACE_(my_ITaskbarList3, IUnknown) {
|
|
STDMETHOD (QueryInterface) (THIS_ REFIID riid, LPVOID *ppvObj) PURE;
|
|
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
|
|
STDMETHOD_(ULONG, Release) (THIS) PURE;
|
|
STDMETHOD (HrInit) (THIS) PURE;
|
|
STDMETHOD (AddTab) (THIS_ HWND hwnd) PURE;
|
|
STDMETHOD (DeleteTab) (THIS_ HWND hwnd) PURE;
|
|
STDMETHOD (ActivateTab) (THIS_ HWND hwnd) PURE;
|
|
STDMETHOD (SetActiveAlt) (THIS_ HWND hwnd) PURE;
|
|
STDMETHOD (MarkFullscreenWindow) (THIS_ HWND hwnd, int fFullscreen) PURE;
|
|
STDMETHOD (SetProgressValue) (THIS_ HWND hwnd, ULONGLONG ullCompleted, ULONGLONG ullTotal) PURE;
|
|
STDMETHOD (SetProgressState) (THIS_ HWND hwnd, TASKBAR_PROGRESS_FLAGS tbpFlags) PURE;
|
|
STDMETHOD (RegisterTab) (THIS_ HWND hwndTab,HWND hwndMDI) PURE;
|
|
STDMETHOD (UnregisterTab) (THIS_ HWND hwndTab) PURE;
|
|
STDMETHOD (SetTabOrder) (THIS_ HWND hwndTab, HWND hwndInsertBefore) PURE;
|
|
STDMETHOD (SetTabActive) (THIS_ HWND hwndTab, HWND hwndMDI, DWORD dwReserved) PURE;
|
|
STDMETHOD (ThumbBarAddButtons) (THIS_ HWND hwnd, UINT cButtons, MY_THUMBBUTTON* pButton) PURE;
|
|
STDMETHOD (ThumbBarUpdateButtons) (THIS_ HWND hwnd, UINT cButtons, MY_THUMBBUTTON* pButton) PURE;
|
|
STDMETHOD (ThumbBarSetImageList) (THIS_ HWND hwnd, HIMAGELIST himl) PURE;
|
|
STDMETHOD (SetOverlayIcon) (THIS_ HWND hwnd, HICON hIcon, LPCWSTR pszDescription) PURE;
|
|
STDMETHOD (SetThumbnailTooltip) (THIS_ HWND hwnd, LPCWSTR pszTip) PURE;
|
|
STDMETHOD (SetThumbnailClip) (THIS_ HWND hwnd, RECT *prcClip) PURE;
|
|
};
|
|
const IID my_IID_ITaskbarList3 =
|
|
{ 0xea1afb91, 0x9e28, 0x4b86, { 0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf } };
|
|
const IID my_CLSID_TaskbarList =
|
|
{ 0x56fdf344, 0xfd6d, 0x11d0, { 0x95, 0x8a ,0x0, 0x60, 0x97, 0xc9, 0xa0 ,0x90 } };
|
|
|
|
static my_ITaskbarList3* ptbl = NULL;
|
|
|
|
// Create a taskbar icon progressbar
|
|
BOOL CreateTaskbarList(void)
|
|
{
|
|
HRESULT hr;
|
|
if (nWindowsVersion < WINDOWS_7)
|
|
// Only valid for Windows 7 or later
|
|
return FALSE;
|
|
hr = CoCreateInstance(&my_CLSID_TaskbarList, NULL, CLSCTX_ALL, &my_IID_ITaskbarList3, (LPVOID)&ptbl);
|
|
if (FAILED(hr)) {
|
|
uprintf("CoCreateInstance for TaskbarList failed: error %X\n", hr);
|
|
ptbl = NULL;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetTaskbarProgressState(TASKBAR_PROGRESS_FLAGS tbpFlags)
|
|
{
|
|
if (ptbl == NULL)
|
|
return FALSE;
|
|
return !FAILED(ptbl->lpVtbl->SetProgressState(ptbl, hMainDialog, tbpFlags));
|
|
}
|
|
|
|
BOOL SetTaskbarProgressValue(ULONGLONG ullCompleted, ULONGLONG ullTotal)
|
|
{
|
|
if (ptbl == NULL)
|
|
return FALSE;
|
|
return !FAILED(ptbl->lpVtbl->SetProgressValue(ptbl, hMainDialog, ullCompleted, ullTotal));
|
|
}
|
|
#pragma pop_macro("INTERFACE")
|
|
|
|
|
|
/*
|
|
* Update policy and settings dialog callback
|
|
*/
|
|
INT_PTR CALLBACK UpdateCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int dy;
|
|
RECT rect;
|
|
REQRESIZE* rsz;
|
|
HWND hPolicy;
|
|
static HWND hFrequency, hBeta;
|
|
int32_t freq;
|
|
char update_policy_text[4096];
|
|
static BOOL resized_already = TRUE;
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
resized_already = FALSE;
|
|
hUpdatesDlg = hDlg;
|
|
apply_localization(IDD_UPDATE_POLICY, hDlg);
|
|
SetTitleBarIcon(hDlg);
|
|
CenterDialog(hDlg);
|
|
hFrequency = GetDlgItem(hDlg, IDC_UPDATE_FREQUENCY);
|
|
hBeta = GetDlgItem(hDlg, IDC_INCLUDE_BETAS);
|
|
IGNORE_RETVAL(ComboBox_SetItemData(hFrequency, ComboBox_AddStringU(hFrequency, lmprintf(MSG_013)), -1));
|
|
IGNORE_RETVAL(ComboBox_SetItemData(hFrequency, ComboBox_AddStringU(hFrequency, lmprintf(MSG_030, lmprintf(MSG_014))), 86400));
|
|
IGNORE_RETVAL(ComboBox_SetItemData(hFrequency, ComboBox_AddStringU(hFrequency, lmprintf(MSG_015)), 604800));
|
|
IGNORE_RETVAL(ComboBox_SetItemData(hFrequency, ComboBox_AddStringU(hFrequency, lmprintf(MSG_016)), 2629800));
|
|
freq = ReadSetting32(SETTING_UPDATE_INTERVAL);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_CHECK_NOW), (freq != 0));
|
|
EnableWindow(hBeta, (freq >= 0));
|
|
switch(freq) {
|
|
case -1:
|
|
IGNORE_RETVAL(ComboBox_SetCurSel(hFrequency, 0));
|
|
break;
|
|
case 0:
|
|
case 86400:
|
|
IGNORE_RETVAL(ComboBox_SetCurSel(hFrequency, 1));
|
|
break;
|
|
case 604800:
|
|
IGNORE_RETVAL(ComboBox_SetCurSel(hFrequency, 2));
|
|
break;
|
|
case 2629800:
|
|
IGNORE_RETVAL(ComboBox_SetCurSel(hFrequency, 3));
|
|
break;
|
|
default:
|
|
IGNORE_RETVAL(ComboBox_SetItemData(hFrequency, ComboBox_AddStringU(hFrequency, lmprintf(MSG_017)), freq));
|
|
IGNORE_RETVAL(ComboBox_SetCurSel(hFrequency, 4));
|
|
break;
|
|
}
|
|
IGNORE_RETVAL(ComboBox_AddStringU(hBeta, lmprintf(MSG_008)));
|
|
IGNORE_RETVAL(ComboBox_AddStringU(hBeta, lmprintf(MSG_009)));
|
|
IGNORE_RETVAL(ComboBox_SetCurSel(hBeta, ReadSettingBool(SETTING_INCLUDE_BETAS)?0:1));
|
|
hPolicy = GetDlgItem(hDlg, IDC_POLICY);
|
|
SendMessage(hPolicy, EM_AUTOURLDETECT, 1, 0);
|
|
static_sprintf(update_policy_text, update_policy, lmprintf(MSG_179|MSG_RTF),
|
|
lmprintf(MSG_180|MSG_RTF), lmprintf(MSG_181|MSG_RTF), lmprintf(MSG_182|MSG_RTF), lmprintf(MSG_183|MSG_RTF),
|
|
lmprintf(MSG_184|MSG_RTF), lmprintf(MSG_185|MSG_RTF), lmprintf(MSG_186|MSG_RTF));
|
|
SendMessageA(hPolicy, EM_SETTEXTEX, (WPARAM)&friggin_microsoft_unicode_amateurs, (LPARAM)update_policy_text);
|
|
SendMessage(hPolicy, EM_SETSEL, -1, -1);
|
|
SendMessage(hPolicy, EM_SETEVENTMASK, 0, ENM_LINK|ENM_REQUESTRESIZE);
|
|
SendMessageA(hPolicy, EM_SETBKGNDCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNFACE));
|
|
SendMessage(hPolicy, EM_REQUESTRESIZE, 0, 0);
|
|
break;
|
|
case WM_NOTIFY:
|
|
if ((((LPNMHDR)lParam)->code == EN_REQUESTRESIZE) && (!resized_already)) {
|
|
resized_already = TRUE;
|
|
hPolicy = GetDlgItem(hDlg, IDC_POLICY);
|
|
GetWindowRect(hPolicy, &rect);
|
|
dy = rect.bottom - rect.top;
|
|
rsz = (REQRESIZE *)lParam;
|
|
dy -= rsz->rc.bottom - rsz->rc.top + 6; // add the border
|
|
ResizeMoveCtrl(hDlg, hDlg, 0, 0, 0, -dy, 1.0f);
|
|
ResizeMoveCtrl(hDlg, hPolicy, 0, 0, 0, -dy, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDS_UPDATE_SETTINGS_GRP), 0, -dy, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDS_UPDATE_FREQUENCY_TXT), 0, -dy, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_UPDATE_FREQUENCY), 0, -dy, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDS_INCLUDE_BETAS_TXT), 0, -dy, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_INCLUDE_BETAS), 0, -dy, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDS_CHECK_NOW_GRP), 0, -dy, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_CHECK_NOW), 0, -dy, 0, 0, 1.0f);
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDCANCEL), 0, -dy, 0, 0, 1.0f);
|
|
}
|
|
break;
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDCLOSE:
|
|
case IDCANCEL:
|
|
reset_localization(IDD_UPDATE_POLICY);
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
hUpdatesDlg = NULL;
|
|
return (INT_PTR)TRUE;
|
|
case IDC_CHECK_NOW:
|
|
CheckForUpdates(TRUE);
|
|
return (INT_PTR)TRUE;
|
|
case IDC_UPDATE_FREQUENCY:
|
|
if (HIWORD(wParam) != CBN_SELCHANGE)
|
|
break;
|
|
freq = (int32_t)ComboBox_GetItemData(hFrequency, ComboBox_GetCurSel(hFrequency));
|
|
WriteSetting32(SETTING_UPDATE_INTERVAL, (DWORD)freq);
|
|
EnableWindow(hBeta, (freq >= 0));
|
|
return (INT_PTR)TRUE;
|
|
case IDC_INCLUDE_BETAS:
|
|
if (HIWORD(wParam) != CBN_SELCHANGE)
|
|
break;
|
|
WriteSettingBool(SETTING_INCLUDE_BETAS, ComboBox_GetCurSel(hBeta) == 0);
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
|
|
/*
|
|
* Initial update check setup
|
|
*/
|
|
BOOL SetUpdateCheck(void)
|
|
{
|
|
BOOL enable_updates;
|
|
uint64_t commcheck = _GetTickCount64();
|
|
notification_info more_info = { IDD_UPDATE_POLICY, UpdateCallback };
|
|
char filename[MAX_PATH] = "", exename[] = APPLICATION_NAME ".exe";
|
|
size_t fn_len, exe_len;
|
|
|
|
// Test if we can read and write settings. If not, forget it.
|
|
WriteSetting64(SETTING_COMM_CHECK, commcheck);
|
|
if (ReadSetting64(SETTING_COMM_CHECK) != commcheck)
|
|
return FALSE;
|
|
settings_commcheck = TRUE;
|
|
|
|
// If the update interval is not set, this is the first time we run so prompt the user
|
|
if (ReadSetting32(SETTING_UPDATE_INTERVAL) == 0) {
|
|
|
|
// Add a hack for people who'd prefer the app not to prompt about update settings on first run.
|
|
// If the executable is called "rufus.exe", without version, we disable the prompt
|
|
GetModuleFileNameU(NULL, filename, sizeof(filename));
|
|
fn_len = safe_strlen(filename);
|
|
exe_len = safe_strlen(exename);
|
|
#if !defined(_DEBUG) // Don't allow disabling update prompt, unless it's a release
|
|
if ((fn_len > exe_len) && (safe_stricmp(&filename[fn_len-exe_len], exename) == 0)) {
|
|
uprintf("Short name used - Disabling initial update policy prompt\n");
|
|
enable_updates = TRUE;
|
|
} else {
|
|
#endif
|
|
enable_updates = Notification(MSG_QUESTION, &more_info, lmprintf(MSG_004), lmprintf(MSG_005));
|
|
#if !defined(_DEBUG)
|
|
}
|
|
#endif
|
|
if (!enable_updates) {
|
|
WriteSetting32(SETTING_UPDATE_INTERVAL, -1);
|
|
return FALSE;
|
|
}
|
|
// If the user hasn't set the interval in the dialog, set to default
|
|
if ( (ReadSetting32(SETTING_UPDATE_INTERVAL) == 0) ||
|
|
((ReadSetting32(SETTING_UPDATE_INTERVAL) == -1) && enable_updates) )
|
|
WriteSetting32(SETTING_UPDATE_INTERVAL, 86400);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void CreateStaticFont(HDC dc, HFONT* hyperlink_font) {
|
|
TEXTMETRIC tm;
|
|
LOGFONT lf;
|
|
|
|
if (*hyperlink_font != NULL)
|
|
return;
|
|
GetTextMetrics(dc, &tm);
|
|
lf.lfHeight = tm.tmHeight;
|
|
lf.lfWidth = 0;
|
|
lf.lfEscapement = 0;
|
|
lf.lfOrientation = 0;
|
|
lf.lfWeight = tm.tmWeight;
|
|
lf.lfItalic = tm.tmItalic;
|
|
lf.lfUnderline = TRUE;
|
|
lf.lfStrikeOut = tm.tmStruckOut;
|
|
lf.lfCharSet = tm.tmCharSet;
|
|
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
|
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
lf.lfQuality = DEFAULT_QUALITY;
|
|
lf.lfPitchAndFamily = tm.tmPitchAndFamily;
|
|
GetTextFace(dc, LF_FACESIZE, lf.lfFaceName);
|
|
*hyperlink_font = CreateFontIndirect(&lf);
|
|
}
|
|
|
|
/*
|
|
* Work around the limitations of edit control, to display a hand cursor for hyperlinks
|
|
* NB: The LTEXT control must have SS_NOTIFY attribute for this to work
|
|
*/
|
|
INT_PTR CALLBACK update_subclass_callback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_SETCURSOR:
|
|
if ((HWND)wParam == GetDlgItem(hDlg, IDC_WEBSITE)) {
|
|
SetCursor(LoadCursor(NULL, IDC_HAND));
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return CallWindowProc(update_original_proc, hDlg, message, wParam, lParam);
|
|
}
|
|
|
|
/*
|
|
* New version notification dialog
|
|
*/
|
|
INT_PTR CALLBACK NewVersionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
char cmdline[] = APPLICATION_NAME " -w 150";
|
|
static char* filepath = NULL;
|
|
static int download_status = 0;
|
|
LONG i;
|
|
HWND hNotes;
|
|
STARTUPINFOA si;
|
|
PROCESS_INFORMATION pi;
|
|
HFONT hyperlink_font = NULL;
|
|
EXT_DECL(dl_ext, NULL, __VA_GROUP__("*.exe"), __VA_GROUP__(lmprintf(MSG_037)));
|
|
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
apply_localization(IDD_NEW_VERSION, hDlg);
|
|
download_status = 0;
|
|
SetTitleBarIcon(hDlg);
|
|
CenterDialog(hDlg);
|
|
// Subclass the callback so that we can change the cursor
|
|
update_original_proc = (WNDPROC)SetWindowLongPtr(hDlg, GWLP_WNDPROC, (LONG_PTR)update_subclass_callback);
|
|
hNotes = GetDlgItem(hDlg, IDC_RELEASE_NOTES);
|
|
SendMessage(hNotes, EM_AUTOURLDETECT, 1, 0);
|
|
SendMessageA(hNotes, EM_SETTEXTEX, (WPARAM)&friggin_microsoft_unicode_amateurs, (LPARAM)update.release_notes);
|
|
SendMessage(hNotes, EM_SETSEL, -1, -1);
|
|
SendMessage(hNotes, EM_SETEVENTMASK, 0, ENM_LINK);
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_YOUR_VERSION), lmprintf(MSG_018,
|
|
rufus_version[0], rufus_version[1], rufus_version[2]));
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_LATEST_VERSION), lmprintf(MSG_019,
|
|
update.version[0], update.version[1], update.version[2]));
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_DOWNLOAD_URL), update.download_url);
|
|
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS), PBM_SETRANGE, 0, (MAX_PROGRESS<<16) & 0xFFFF0000);
|
|
if (update.download_url == NULL)
|
|
EnableWindow(GetDlgItem(hDlg, IDC_DOWNLOAD), FALSE);
|
|
break;
|
|
case WM_CTLCOLORSTATIC:
|
|
if ((HWND)lParam != GetDlgItem(hDlg, IDC_WEBSITE))
|
|
return FALSE;
|
|
// Change the font for the hyperlink
|
|
SetBkMode((HDC)wParam, TRANSPARENT);
|
|
CreateStaticFont((HDC)wParam, &hyperlink_font);
|
|
SelectObject((HDC)wParam, hyperlink_font);
|
|
SetTextColor((HDC)wParam, RGB(0,0,125)); // DARK_BLUE
|
|
return (INT_PTR)CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDCLOSE:
|
|
case IDCANCEL:
|
|
if (download_status != 1) {
|
|
reset_localization(IDD_NEW_VERSION);
|
|
safe_free(filepath);
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
}
|
|
return (INT_PTR)TRUE;
|
|
case IDC_WEBSITE:
|
|
ShellExecuteA(hDlg, "open", RUFUS_URL, NULL, NULL, SW_SHOWNORMAL);
|
|
break;
|
|
case IDC_DOWNLOAD: // Also doubles as abort and launch function
|
|
switch(download_status) {
|
|
case 1: // Abort
|
|
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANCELLED;
|
|
download_status = 0;
|
|
break;
|
|
case 2: // Launch newer version and close this one
|
|
Sleep(1000); // Add a delay on account of antivirus scanners
|
|
|
|
if (ValidateSignature(hDlg, filepath) != NO_ERROR) {
|
|
// Unconditionally delete the download and disable the "Launch" control
|
|
_unlinkU(filepath);
|
|
EnableWindow(GetDlgItem(hDlg, IDC_DOWNLOAD), FALSE);
|
|
break;
|
|
}
|
|
|
|
memset(&si, 0, sizeof(si));
|
|
memset(&pi, 0, sizeof(pi));
|
|
si.cb = sizeof(si);
|
|
if (!CreateProcessU(filepath, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
|
|
PrintInfo(0, MSG_214);
|
|
uprintf("Failed to launch new application: %s\n", WindowsErrorString());
|
|
} else {
|
|
PrintInfo(0, MSG_213);
|
|
PostMessage(hDlg, WM_COMMAND, (WPARAM)IDCLOSE, 0);
|
|
PostMessage(hMainDialog, WM_CLOSE, 0, 0);
|
|
}
|
|
break;
|
|
default: // Download
|
|
if (update.download_url == NULL) {
|
|
uprintf("Could not get download URL\n");
|
|
break;
|
|
}
|
|
for (i=(int)strlen(update.download_url); (i>0)&&(update.download_url[i]!='/'); i--);
|
|
dl_ext.filename = &update.download_url[i+1];
|
|
filepath = FileDialog(TRUE, app_dir, &dl_ext, OFN_NOCHANGEDIR);
|
|
if (filepath == NULL) {
|
|
uprintf("Could not get save path\n");
|
|
break;
|
|
}
|
|
// Opening the File Dialog will make us lose tabbing focus - set it back
|
|
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDC_DOWNLOAD), TRUE);
|
|
DownloadFileThreaded(update.download_url, filepath, hDlg);
|
|
break;
|
|
}
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
break;
|
|
case UM_PROGRESS_INIT:
|
|
EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_DOWNLOAD), lmprintf(MSG_038));
|
|
FormatStatus = 0;
|
|
download_status = 1;
|
|
return (INT_PTR)TRUE;
|
|
case UM_PROGRESS_EXIT:
|
|
EnableWindow(GetDlgItem(hDlg, IDCANCEL), TRUE);
|
|
if (wParam) {
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_DOWNLOAD), lmprintf(MSG_039));
|
|
download_status = 2;
|
|
} else {
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_DOWNLOAD), lmprintf(MSG_040));
|
|
download_status = 0;
|
|
}
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
return (INT_PTR)FALSE;
|
|
}
|
|
|
|
void DownloadNewVersion(void)
|
|
{
|
|
MyDialogBox(hMainInstance, IDD_NEW_VERSION, hMainDialog, NewVersionCallback);
|
|
}
|
|
|
|
void SetTitleBarIcon(HWND hDlg)
|
|
{
|
|
int i16, s16, s32;
|
|
HICON hSmallIcon, hBigIcon;
|
|
|
|
// High DPI scaling
|
|
i16 = GetSystemMetrics(SM_CXSMICON);
|
|
// Adjust icon size lookup
|
|
s16 = i16;
|
|
s32 = (int)(32.0f*fScale);
|
|
if (s16 >= 54)
|
|
s16 = 64;
|
|
else if (s16 >= 40)
|
|
s16 = 48;
|
|
else if (s16 >= 28)
|
|
s16 = 32;
|
|
else if (s16 >= 20)
|
|
s16 = 24;
|
|
if (s32 >= 54)
|
|
s32 = 64;
|
|
else if (s32 >= 40)
|
|
s32 = 48;
|
|
else if (s32 >= 28)
|
|
s32 = 32;
|
|
else if (s32 >= 20)
|
|
s32 = 24;
|
|
|
|
// Create the title bar icon
|
|
hSmallIcon = (HICON)LoadImage(hMainInstance, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, s16, s16, 0);
|
|
SendMessage (hDlg, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon);
|
|
hBigIcon = (HICON)LoadImage(hMainInstance, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, s32, s32, 0);
|
|
SendMessage (hDlg, WM_SETICON, ICON_BIG, (LPARAM)hBigIcon);
|
|
}
|
|
|
|
// Return the onscreen size of the text displayed by a control
|
|
SIZE GetTextSize(HWND hCtrl)
|
|
{
|
|
SIZE sz = {0, 0};
|
|
HDC hDC;
|
|
wchar_t *wstr = NULL;
|
|
int len;
|
|
HFONT hFont;
|
|
|
|
// Compute the size of the text
|
|
hDC = GetDC(hCtrl);
|
|
if (hDC == NULL)
|
|
goto out;
|
|
hFont = (HFONT)SendMessageA(hCtrl, WM_GETFONT, 0, 0);
|
|
if (hFont == NULL)
|
|
goto out;
|
|
SelectObject(hDC, hFont);
|
|
len = GetWindowTextLengthW(hCtrl);
|
|
if (len <= 0)
|
|
goto out;
|
|
wstr = calloc(len + 1, sizeof(wchar_t));
|
|
if (wstr == NULL)
|
|
goto out;
|
|
if (GetWindowTextW(hCtrl, wstr, len + 1) > 0)
|
|
GetTextExtentPoint32W(hDC, wstr, len, &sz);
|
|
out:
|
|
safe_free(wstr);
|
|
if (hDC != NULL)
|
|
ReleaseDC(hCtrl, hDC);
|
|
return sz;
|
|
}
|
|
|
|
/*
|
|
* The following is used to work around dialog template limitations when switching from LTR to RTL
|
|
* or switching the font. This avoids having to multiply similar templates in the RC.
|
|
* TODO: Can we use http://stackoverflow.com/questions/6057239/which-font-is-the-default-for-mfc-dialog-controls?
|
|
* TODO: We are supposed to use Segoe with font size 9 in Vista or later
|
|
*/
|
|
|
|
// Produce a dialog template from our RC, and update its RTL and Font settings dynamically
|
|
// See http://blogs.msdn.com/b/oldnewthing/archive/2004/06/21/163596.aspx as well as
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms645389.aspx for a description
|
|
// of the Dialog structure
|
|
LPCDLGTEMPLATE GetDialogTemplate(int Dialog_ID)
|
|
{
|
|
int i;
|
|
const char thai_id[] = "th-TH";
|
|
size_t len;
|
|
DWORD size;
|
|
DWORD* dwBuf;
|
|
WCHAR* wBuf;
|
|
LPCDLGTEMPLATE rcTemplate = (LPCDLGTEMPLATE) GetResource(hMainInstance, MAKEINTRESOURCEA(Dialog_ID),
|
|
_RT_DIALOG, get_name_from_id(Dialog_ID), &size, TRUE);
|
|
|
|
if ((size == 0) || (rcTemplate == NULL)) {
|
|
safe_free(rcTemplate);
|
|
return NULL;
|
|
}
|
|
if (right_to_left_mode) {
|
|
// Add the RTL styles into our RC copy, so that we don't have to multiply dialog definitions in the RC
|
|
dwBuf = (DWORD*)rcTemplate;
|
|
dwBuf[2] = WS_EX_RTLREADING | WS_EX_APPWINDOW | WS_EX_LAYOUTRTL;
|
|
}
|
|
|
|
// All our dialogs are set to use 'Segoe UI Symbol' by default:
|
|
// 1. So that we can replace the font name with 'MS Shell Dlg' (XP) or 'Segoe UI'
|
|
// 2. So that Thai displays properly on RTF controls as it won't work with regular
|
|
// 'Segoe UI'... but Cyrillic won't work with 'Segoe UI Symbol'
|
|
|
|
// If 'Segoe UI Symbol' is available, and we are using Thai, we're done here
|
|
if (IsFontAvailable("Segoe UI Symbol") && (selected_locale != NULL)
|
|
&& (safe_strcmp(selected_locale->txt[0], thai_id) == 0))
|
|
return rcTemplate;
|
|
|
|
// 'Segoe UI Symbol' cannot be used => Fall back to the best we have
|
|
wBuf = (WCHAR*)rcTemplate;
|
|
wBuf = &wBuf[14]; // Move to class name
|
|
// Skip class name and title
|
|
for (i = 0; i<2; i++) {
|
|
if (*wBuf == 0xFFFF)
|
|
wBuf = &wBuf[2]; // Ordinal
|
|
else
|
|
wBuf = &wBuf[wcslen(wBuf) + 1]; // String
|
|
}
|
|
// NB: to change the font size to 9, you can use
|
|
// wBuf[0] = 0x0009;
|
|
wBuf = &wBuf[3];
|
|
// Make sure we are where we want to be and adjust the font
|
|
if (wcscmp(L"Segoe UI Symbol", wBuf) == 0) {
|
|
uintptr_t src, dst, start = (uintptr_t)rcTemplate;
|
|
// We can't simply zero the characters we don't want, as the size of the font
|
|
// string determines the next item lookup. So we must memmove the remaining of
|
|
// our buffer. Oh, and those items are DWORD aligned.
|
|
if ((nWindowsVersion > WINDOWS_XP) && IsFontAvailable("Segoe UI")) {
|
|
// 'Segoe UI Symbol' -> 'Segoe UI'
|
|
wBuf[8] = 0;
|
|
} else {
|
|
wcscpy(wBuf, L"MS Shell Dlg");
|
|
}
|
|
len = wcslen(wBuf);
|
|
wBuf[len + 1] = 0;
|
|
dst = (uintptr_t)&wBuf[len + 2];
|
|
dst &= ~3;
|
|
src = (uintptr_t)&wBuf[17];
|
|
src &= ~3;
|
|
memmove((void*)dst, (void*)src, size - (src - start));
|
|
} else {
|
|
uprintf("Could not locate font for %s!", get_name_from_id(Dialog_ID));
|
|
}
|
|
return rcTemplate;
|
|
}
|
|
|
|
HWND MyCreateDialog(HINSTANCE hInstance, int Dialog_ID, HWND hWndParent, DLGPROC lpDialogFunc)
|
|
{
|
|
LPCDLGTEMPLATE rcTemplate = GetDialogTemplate(Dialog_ID);
|
|
HWND hDlg = CreateDialogIndirect(hInstance, rcTemplate, hWndParent, lpDialogFunc);
|
|
safe_free(rcTemplate);
|
|
return hDlg;
|
|
}
|
|
|
|
INT_PTR MyDialogBox(HINSTANCE hInstance, int Dialog_ID, HWND hWndParent, DLGPROC lpDialogFunc)
|
|
{
|
|
INT_PTR ret;
|
|
LPCDLGTEMPLATE rcTemplate = GetDialogTemplate(Dialog_ID);
|
|
|
|
// A DialogBox doesn't handle reduce/restore so it won't pass restore messages to the
|
|
// main dialog if the main dialog was minimized. This can result in situations where the
|
|
// user cannot restore the main window if a new dialog prompt was triggered while the
|
|
// main dialog was reduced => Ensure the main dialog is visible before we display anything.
|
|
ShowWindow(hMainDialog, SW_NORMAL);
|
|
|
|
ret = DialogBoxIndirect(hMainInstance, rcTemplate, hWndParent, lpDialogFunc);
|
|
safe_free(rcTemplate);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* The following function calls are used to automatically detect and close the native
|
|
* Windows format prompt "You must format the disk in drive X:". To do that, we use an
|
|
* event hook that gets triggered whenever a window is placed in the foreground.
|
|
* In that hook, we look for a dialog that has style WS_POPUPWINDOW and has the relevant
|
|
* title. However, because the title in itself is too generic (the expectation is that
|
|
* it will be "Microsoft Windows") we also enumerate all the child controls from that
|
|
* prompt, using another callback, until we find one that contains the text we expect
|
|
* for the "Format disk" button.
|
|
* Oh, and since all of these strings are localized, we must first pick them up from
|
|
* the relevant mui (something like "C:\Windows\System32\en-GB\shell32.dll.mui")
|
|
*/
|
|
static BOOL CALLBACK FormatPromptCallback(HWND hWnd, LPARAM lParam)
|
|
{
|
|
char str[128];
|
|
BOOL *found = (BOOL*)lParam;
|
|
|
|
if (GetWindowTextU(hWnd, str, sizeof(str)) == 0)
|
|
return TRUE;
|
|
if (safe_strcmp(str, fp_button_str) == 0)
|
|
*found = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
static void CALLBACK FormatPromptHook(HWINEVENTHOOK hWinEventHook, DWORD Event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
|
|
{
|
|
char str[128];
|
|
BOOL found;
|
|
|
|
if (Event == EVENT_SYSTEM_FOREGROUND) {
|
|
if (GetWindowLong(hWnd, GWL_STYLE) & WS_POPUPWINDOW) {
|
|
str[0] = 0;
|
|
GetWindowTextU(hWnd, str, sizeof(str));
|
|
if (safe_strcmp(str, fp_title_str) == 0) {
|
|
found = FALSE;
|
|
EnumChildWindows(hWnd, FormatPromptCallback, (LPARAM)&found);
|
|
if (found) {
|
|
SendMessage(hWnd, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0);
|
|
uprintf("Closed Windows format prompt");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL SetFormatPromptHook(void)
|
|
{
|
|
HMODULE mui_lib;
|
|
char mui_path[MAX_PATH];
|
|
static char title_str[128], button_str[128];
|
|
|
|
if (fp_weh != NULL)
|
|
return TRUE; // No need to set again if active
|
|
|
|
// Fetch the localized strings in the relevant
|
|
static_sprintf(mui_path, "%s\\%s\\shell32.dll.mui", system_dir, GetCurrentMUI());
|
|
mui_lib = LoadLibraryU(mui_path);
|
|
if (mui_lib != NULL) {
|
|
// 4097 = "You need to format the disk in drive %c: before you can use it." (dialog text)
|
|
// 4125 = "Microsoft Windows" (dialog title)
|
|
// 4126 = "Format disk" (button)
|
|
if (LoadStringU(mui_lib, 4125, title_str, sizeof(title_str)) > 0)
|
|
fp_title_str = title_str;
|
|
else
|
|
uprintf("Warning: Could not locate localized format prompt title string in '%s': %s", mui_path, WindowsErrorString());
|
|
if (LoadStringU(mui_lib, 4126, button_str, sizeof(button_str)) > 0)
|
|
fp_button_str = button_str;
|
|
else
|
|
uprintf("Warning: Could not locate localized format prompt button string in '%s': %s", mui_path, WindowsErrorString());
|
|
FreeLibrary(mui_lib);
|
|
}
|
|
|
|
fp_weh = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, NULL,
|
|
FormatPromptHook, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
|
return (fp_weh != NULL);
|
|
}
|
|
|
|
void ClrFormatPromptHook(void) {
|
|
UnhookWinEvent(fp_weh);
|
|
fp_weh = NULL;
|
|
}
|
|
|
|
void FlashTaskbar(HANDLE handle)
|
|
{
|
|
FLASHWINFO pf;
|
|
|
|
if (handle == NULL)
|
|
return;
|
|
pf.cbSize = sizeof(FLASHWINFO);
|
|
pf.hwnd = handle;
|
|
// Could also use FLASHW_ALL to flash the main dialog)
|
|
pf.dwFlags = FLASHW_TIMER | FLASHW_TRAY;
|
|
pf.uCount = 5;
|
|
pf.dwTimeout = 75;
|
|
FlashWindowEx(&pf);
|
|
}
|
|
|
|
#ifdef RUFUS_TEST
|
|
static __inline LPWORD lpwAlign(LPWORD addr)
|
|
{
|
|
return (LPWORD)((((uintptr_t)addr) + 3) & (~3));
|
|
}
|
|
|
|
INT_PTR CALLBACK SelectionDynCallback(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int r = -1;
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
return (INT_PTR)TRUE;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDOK:
|
|
r = 0;
|
|
case IDCANCEL:
|
|
EndDialog(hwndDlg, r);
|
|
return (INT_PTR)TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int SelectionDyn(char* title, char* message, char** szChoice, int nChoices)
|
|
{
|
|
#define ID_RADIO 12345
|
|
LPCWSTR lpwszTypeFace = L"MS Shell Dlg";
|
|
LPDLGTEMPLATEA lpdt;
|
|
LPDLGITEMTEMPLATEA lpdit;
|
|
LPCWSTR lpwszCaption;
|
|
LPWORD lpw;
|
|
LPWSTR lpwsz;
|
|
int i, ret, nchar;
|
|
|
|
lpdt = (LPDLGTEMPLATE)calloc(512 + nChoices * 256, 1);
|
|
|
|
// Set up a dialog window
|
|
lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION | DS_MODALFRAME | DS_CENTER | DS_SHELLFONT;
|
|
lpdt->cdit = 2 + nChoices;
|
|
lpdt->x = 10;
|
|
lpdt->y = 10;
|
|
lpdt->cx = 300;
|
|
lpdt->cy = 100;
|
|
|
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms645394.aspx:
|
|
// In a standard template for a dialog box, the DLGTEMPLATE structure is always immediately followed by
|
|
// three variable-length arrays that specify the menu, class, and title for the dialog box.
|
|
// When the DS_SETFONT style is specified, these arrays are also followed by a 16-bit value specifying
|
|
// point size and another variable-length array specifying a typeface name. Each array consists of one
|
|
// or more 16-bit elements. The menu, class, title, and font arrays must be aligned on WORD boundaries.
|
|
lpw = (LPWORD)(&lpdt[1]);
|
|
*lpw++ = 0; // No menu
|
|
*lpw++ = 0; // Default dialog class
|
|
lpwsz = (LPWSTR)lpw;
|
|
nchar = MultiByteToWideChar(CP_UTF8, 0, title, -1, lpwsz, 50);
|
|
lpw += nchar;
|
|
|
|
// Set point size and typeface name if required
|
|
if (lpdt->style & (DS_SETFONT | DS_SHELLFONT)) {
|
|
*lpw++ = 8;
|
|
for (lpwsz = (LPWSTR)lpw, lpwszCaption = lpwszTypeFace; (*lpwsz++ = *lpwszCaption++) != 0; );
|
|
lpw = (LPWORD)lpwsz;
|
|
}
|
|
|
|
// Add an OK button
|
|
lpw = lpwAlign(lpw);
|
|
lpdit = (LPDLGITEMTEMPLATE)lpw;
|
|
lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;
|
|
lpdit->x = 10;
|
|
lpdit->y = 70;
|
|
lpdit->cx = 50;
|
|
lpdit->cy = 14;
|
|
lpdit->id = IDOK;
|
|
|
|
lpw = (LPWORD)(&lpdit[1]);
|
|
*lpw++ = 0xFFFF;
|
|
*lpw++ = 0x0080; // Button class
|
|
|
|
lpwsz = (LPWSTR)lpw;
|
|
nchar = MultiByteToWideChar(CP_UTF8, 0, "OK", -1, lpwsz, 50);
|
|
lpw += nchar;
|
|
*lpw++ = 0; // No creation data
|
|
|
|
// Add a Cancel button
|
|
lpw = lpwAlign(lpw);
|
|
lpdit = (LPDLGITEMTEMPLATE)lpw;
|
|
lpdit->x = 90;
|
|
lpdit->y = 70;
|
|
lpdit->cx = 50;
|
|
lpdit->cy = 14;
|
|
lpdit->id = IDCANCEL;
|
|
lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
|
|
|
|
lpw = (LPWORD)(&lpdit[1]);
|
|
*lpw++ = 0xFFFF;
|
|
*lpw++ = 0x0080;
|
|
|
|
lpwsz = (LPWSTR)lpw;
|
|
nchar = MultiByteToWideChar(CP_UTF8, 0, lmprintf(MSG_007), -1, lpwsz, 50);
|
|
lpw += nchar;
|
|
*lpw++ = 0;
|
|
|
|
// Add radio buttons
|
|
for (i = 0; i < nChoices; i++) {
|
|
lpw = lpwAlign(lpw);
|
|
lpdit = (LPDLGITEMTEMPLATE)lpw;
|
|
lpdit->x = 10;
|
|
lpdit->y = 10 + 15 * i;
|
|
lpdit->cx = 40;
|
|
lpdit->cy = 20;
|
|
lpdit->id = ID_RADIO;
|
|
lpdit->style = WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON | (i == 0 ? WS_GROUP : 0);
|
|
|
|
lpw = (LPWORD)(&lpdit[1]);
|
|
*lpw++ = 0xFFFF;
|
|
*lpw++ = 0x0080;
|
|
|
|
lpwsz = (LPWSTR)lpw;
|
|
nchar = MultiByteToWideChar(CP_UTF8, 0, szChoice[i], -1, lpwsz, 150);
|
|
lpw += nchar;
|
|
*lpw++ = 0;
|
|
}
|
|
|
|
ret = (int)DialogBoxIndirect(hMainInstance, (LPDLGTEMPLATE)lpdt, hMainDialog, (DLGPROC)SelectionDynCallback);
|
|
free(lpdt);
|
|
return ret;
|
|
}
|
|
#endif
|