1
1
Fork 0
mirror of https://github.com/pbatard/rufus.git synced 2024-08-14 23:57:05 +00:00
rufus/src/stdlg.c

2134 lines
70 KiB
C
Raw Normal View History

/*
* Rufus: The Reliable USB Formatting Utility
* Standard Dialog Routines (Browse for folder, About, etc)
2017-01-01 15:30:03 +00:00
* 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) {
2016-03-25 16:38:01 +00:00
pfod->lpVtbl->SetFolder(pfod, si_path);
}
if (fname != NULL) {
2016-03-25 16:38:01 +00:00
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;
2016-03-25 16:38:01 +00:00
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;
}
2016-03-25 16:38:01 +00:00
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];
2015-07-09 00:20:21 +00:00
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
2013-10-24 21:57:34 +00:00
* See http://msdn.microsoft.com/en-us/library/windows/desktop/ms644996.aspx#init_box
*/
void CenterDialog(HWND hDlg)
{
HWND hParent;
2013-10-24 21:57:34 +00:00
RECT rc, rcDlg, rcParent;
2013-10-24 21:57:34 +00:00
if ((hParent = GetParent(hDlg)) == NULL) {
hParent = GetDesktopWindow();
}
2013-10-24 21:57:34 +00:00
GetWindowRect(hParent, &rcParent);
GetWindowRect(hDlg, &rcDlg);
CopyRect(&rc, &rcParent);
2013-10-24 21:57:34 +00:00
// 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);
2013-10-24 21:57:34 +00:00
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]),
2017-01-01 15:30:03 +00:00
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) {
2012-05-30 23:32:25 +00:00
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;
}
2013-10-24 21:57:34 +00:00
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;
}
2013-10-24 21:57:34 +00:00
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)
{
2013-10-24 21:57:34 +00:00
int i, j;
2013-10-24 21:57:34 +00:00
for (i=0, j=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == NULL) continue;
2013-10-24 21:57:34 +00:00
j++;
DestroyWindow(ttlist[i].hTip);
safe_free(ttlist[i].wstring);
2013-10-24 21:57:34 +00:00
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)) {
2012-05-30 23:32:25 +00:00
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);
2014-11-27 23:24:50 +00:00
#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 {
2014-11-27 23:24:50 +00:00
#endif
enable_updates = Notification(MSG_QUESTION, &more_info, lmprintf(MSG_004), lmprintf(MSG_005));
2014-11-27 23:24:50 +00:00
#if !defined(_DEBUG)
}
2014-11-27 23:24:50 +00:00
#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)
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;
2016-02-05 22:24:47 +00:00
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)
2016-02-05 22:24:47 +00:00
&& (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