2011-11-21 17:06:17 +00:00
|
|
|
/*
|
2011-12-05 11:36:02 +00:00
|
|
|
* Rufus: The Reliable USB Formatting Utility
|
2011-11-21 17:06:17 +00:00
|
|
|
* Standard Dialog Routines (Browse for folder, About, etc)
|
2019-01-02 11:41:23 +00:00
|
|
|
* Copyright © 2011-2019 Pete Batard <pete@akeo.ie>
|
2011-11-21 17:06:17 +00:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
2011-11-21 20:12:23 +00:00
|
|
|
/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
|
|
|
|
#ifdef _CRTDBG_MAP_ALLOC
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <crtdbg.h>
|
|
|
|
#endif
|
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#include <windowsx.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <shlobj.h>
|
|
|
|
#include <commdlg.h>
|
2012-11-08 20:36:48 +00:00
|
|
|
#include <richedit.h>
|
2018-11-20 12:28:20 +00:00
|
|
|
#include <assert.h>
|
2011-11-21 17:06:17 +00:00
|
|
|
|
|
|
|
#include "rufus.h"
|
2016-02-24 16:10:54 +00:00
|
|
|
#include "missing.h"
|
2016-02-20 22:52:32 +00:00
|
|
|
#include "resource.h"
|
2011-11-21 17:06:17 +00:00
|
|
|
#include "msapi_utf8.h"
|
2016-02-20 22:52:32 +00:00
|
|
|
#include "localization.h"
|
2018-07-06 04:46:36 +00:00
|
|
|
#include "ui.h"
|
2016-02-20 22:52:32 +00:00
|
|
|
|
2012-12-02 03:50:08 +00:00
|
|
|
#include "registry.h"
|
2015-01-25 00:56:38 +00:00
|
|
|
#include "settings.h"
|
2011-11-21 17:06:17 +00:00
|
|
|
#include "license.h"
|
|
|
|
|
|
|
|
/* Globals */
|
[net] add Windows retail ISO downloads
* This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED*
PowerShell script, that is downloaded from GitHub and that resides in memory for
the duration of a session.
* The reason we use a downloaded PS script, rather than an embedded on, is because:
- Microsoft have regularly been changing the deal with regards to how retail ISOs
can be downloaded, and not for the better, so we can't simply embed a static
means of downloading ISOs and expect that to work forever.
- By using an external script, we can immediately respond to whatever new means of
*ANNOYING* their legitimate users Microsoft will come up with next, as well as
make sure that, the minute a new retail version of Windows becomes available, it
also becomes available for download in Rufus.
* Note that if you are concerned about downloading a remote PS script that is being
run at the same level as an elevated application, you should understand that:
- Only scripts downloaded from GitHub, from an account that is protected with 2FA,
are allowed to run (i.e. someone would first have to steal a *physical* 2FA key
to be in a position to upload a malicious script).
- On top of this, only scripts that are signed with a separate private key (RSA +
AES-256), that is itself also protected with a strong unique password which only
a single person knows (and must manually enter each time they want to make a new
version of the script available for download), are allowed to run.
The above means that there's about as much chance for someone to manage to upload
a malicious script on the GitHub servers, that Rufus would allow to run, as there
is for someone to upload a malicious version of Rufus itself.
Still, if you are paranoid and have concerns that, even as you can validate from
its source that Rufus does not attempt to execute any remote script unless a user
actively selected and clicked the DOWNLOAD button, you can also completely disable
the remote script download feature, if you just set the update check to disabled
(which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to
enable or not, the very first time you run the application).
* Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
|
|
|
extern BOOL is_x86_32, enable_fido;
|
2011-11-21 17:06:17 +00:00
|
|
|
static HICON hMessageIcon = (HICON)INVALID_HANDLE_VALUE;
|
|
|
|
static char* szMessageText = NULL;
|
|
|
|
static char* szMessageTitle = NULL;
|
2017-07-11 16:43:51 +00:00
|
|
|
static char **szDialogItem;
|
|
|
|
static int nDialogItems;
|
2019-03-07 16:29:43 +00:00
|
|
|
static HWND hBrowseEdit, hUpdatesDlg;
|
2015-07-06 21:04:38 +00:00
|
|
|
static WNDPROC pOrgBrowseWndproc;
|
2012-11-29 02:59:52 +00:00
|
|
|
static const SETTEXTEX friggin_microsoft_unicode_amateurs = {ST_DEFAULT, CP_UTF8};
|
2012-11-29 23:14:36 +00:00
|
|
|
static BOOL notification_is_question;
|
2012-12-02 03:50:08 +00:00
|
|
|
static const notification_info* notification_more_info;
|
2018-11-20 12:28:20 +00:00
|
|
|
static const char* notification_dont_display_setting;
|
2015-05-14 23:36:42 +00:00
|
|
|
static WNDPROC update_original_proc = NULL;
|
2019-03-05 12:41:10 +00:00
|
|
|
static HWINEVENTHOOK ap_weh = NULL;
|
2019-03-07 16:29:43 +00:00
|
|
|
static char title_str[3][128], button_str[128];
|
|
|
|
HWND hFidoDlg = NULL;
|
2019-03-05 12:41:10 +00:00
|
|
|
BOOL close_fido_cookie_prompts = FALSE;
|
2011-11-21 17:06:17 +00:00
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
static int update_settings_reposition_ids[] = {
|
|
|
|
IDC_POLICY,
|
|
|
|
IDS_UPDATE_SETTINGS_GRP,
|
|
|
|
IDS_UPDATE_FREQUENCY_TXT,
|
|
|
|
IDS_INCLUDE_BETAS_TXT,
|
|
|
|
IDC_UPDATE_FREQUENCY,
|
|
|
|
IDC_INCLUDE_BETAS,
|
|
|
|
IDS_CHECK_NOW_GRP,
|
|
|
|
IDC_CHECK_NOW,
|
|
|
|
IDCANCEL,
|
|
|
|
};
|
2016-01-11 13:06:33 +00:00
|
|
|
|
2016-12-13 14:21:51 +00:00
|
|
|
/*
|
|
|
|
* https://blogs.msdn.microsoft.com/oldnewthing/20040802-00/?p=38283/
|
|
|
|
*/
|
|
|
|
void SetDialogFocus(HWND hDlg, HWND hCtrl)
|
|
|
|
{
|
|
|
|
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hCtrl, TRUE);
|
|
|
|
}
|
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
2016-12-13 14:21:51 +00:00
|
|
|
SetDialogFocus(hDlg, hBrowseEdit);
|
2017-11-13 14:29:48 +00:00
|
|
|
// On Windows 7, MinGW only properly selects the specified folder when using a pidl
|
|
|
|
wpath = utf8_to_wchar(szFolderPath);
|
|
|
|
pidl = SHSimpleIDListFromPath(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);
|
2011-11-21 17:06:17 +00:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
|
2012-12-07 00:54:40 +00:00
|
|
|
dialog_showing++;
|
2017-11-13 14:29:48 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-11-27 11:59:19 +00:00
|
|
|
hr = IFileOpenDialog_SetOptions(pfod, FOS_PICKFOLDERS);
|
2017-11-13 14:29:48 +00:00
|
|
|
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;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
2017-11-13 14:29:48 +00:00
|
|
|
}
|
|
|
|
}
|
2011-11-21 17:06:17 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
hr = SHCreateItemFromParsingName(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
if (wpath != NULL) {
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileOpenDialog_SetFolder(pfod, si_path);
|
2017-11-13 14:29:48 +00:00
|
|
|
}
|
|
|
|
if (fname != NULL) {
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileOpenDialog_SetFileName(pfod, fname);
|
2017-11-13 14:29:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
safe_free(wpath);
|
|
|
|
|
2018-11-27 11:59:19 +00:00
|
|
|
hr = IFileOpenDialog_Show(pfod, hMainDialog);
|
2017-11-13 14:29:48 +00:00
|
|
|
if (SUCCEEDED(hr)) {
|
2018-11-27 11:59:19 +00:00
|
|
|
hr = IFileOpenDialog_GetResult(pfod, &psi);
|
2017-11-13 14:29:48 +00:00
|
|
|
if (SUCCEEDED(hr)) {
|
2018-11-27 11:59:19 +00:00
|
|
|
IShellItem_GetDisplayName(psi, SIGDN_FILESYSPATH, &wpath);
|
2017-11-13 14:29:48 +00:00
|
|
|
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);
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
2017-11-13 14:29:48 +00:00
|
|
|
} else {
|
|
|
|
uprintf("Failed to set folder option for FileOpenDialog: error %X\n", hr);
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
2017-11-13 14:29:48 +00:00
|
|
|
} 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;
|
|
|
|
}
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileOpenDialog_Release(pfod);
|
2017-11-13 14:29:48 +00:00
|
|
|
dialog_showing--;
|
|
|
|
return;
|
2011-11-21 17:06:17 +00:00
|
|
|
fallback:
|
2017-11-13 14:29:48 +00:00
|
|
|
if (pfod != NULL) {
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileOpenDialog_Release(pfod);
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
2016-03-23 16:56:49 +00:00
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
memset(&bi, 0, sizeof(BROWSEINFOW));
|
|
|
|
bi.hwndOwner = hMainDialog;
|
2013-10-15 21:58:27 +00:00
|
|
|
bi.lpszTitle = utf8_to_wchar(lmprintf(MSG_106));
|
2011-11-21 17:06:17 +00:00
|
|
|
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);
|
|
|
|
}
|
2013-10-15 21:58:27 +00:00
|
|
|
safe_free(bi.lpszTitle);
|
2012-12-07 00:54:40 +00:00
|
|
|
dialog_showing--;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
|
2012-02-02 13:16:49 +00:00
|
|
|
/*
|
|
|
|
* Return the UTF8 path of a file selected through a load or save dialog
|
|
|
|
* All string parameters are UTF-8
|
2017-11-13 14:29:48 +00:00
|
|
|
* IMPORTANT NOTE: Remember that you need to call CoInitializeEx() for
|
|
|
|
* *EACH* thread you invoke FileDialog from, as GetDisplayName() will
|
|
|
|
* return error 0x8001010E otherwise.
|
2012-02-02 13:16:49 +00:00
|
|
|
*/
|
2014-05-22 00:52:25 +00:00
|
|
|
char* FileDialog(BOOL save, char* path, const ext_t* ext, DWORD options)
|
2012-02-02 13:16:49 +00:00
|
|
|
{
|
|
|
|
DWORD tmp;
|
|
|
|
OPENFILENAMEA ofn;
|
|
|
|
char selected_name[MAX_PATH];
|
2013-10-15 21:58:27 +00:00
|
|
|
char *ext_string = NULL, *all_files = NULL;
|
2014-05-22 00:52:25 +00:00
|
|
|
size_t i, j, ext_strlen;
|
2012-02-02 13:16:49 +00:00
|
|
|
BOOL r;
|
|
|
|
char* filepath = NULL;
|
|
|
|
HRESULT hr = FALSE;
|
2012-02-24 18:46:02 +00:00
|
|
|
IFileDialog *pfd = NULL;
|
2012-02-02 13:16:49 +00:00
|
|
|
IShellItem *psiResult;
|
2016-03-25 16:38:01 +00:00
|
|
|
COMDLG_FILTERSPEC* filter_spec = NULL;
|
2012-02-02 13:16:49 +00:00
|
|
|
wchar_t *wpath = NULL, *wfilename = NULL;
|
|
|
|
IShellItem *si_path = NULL; // Automatically freed
|
|
|
|
|
2014-05-22 00:52:25 +00:00
|
|
|
if ((ext == NULL) || (ext->count == 0) || (ext->extension == NULL) || (ext->description == NULL))
|
|
|
|
return NULL;
|
2012-12-07 00:54:40 +00:00
|
|
|
dialog_showing++;
|
2014-05-22 00:52:25 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
filter_spec = (COMDLG_FILTERSPEC*)calloc(ext->count + 1, sizeof(COMDLG_FILTERSPEC));
|
|
|
|
if (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));
|
2012-02-02 13:16:49 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
hr = CoCreateInstance(save ? &CLSID_FileSaveDialog : &CLSID_FileOpenDialog, NULL, CLSCTX_INPROC,
|
|
|
|
&IID_IFileDialog, (LPVOID)&pfd);
|
2012-02-02 13:16:49 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
SetLastError(hr);
|
|
|
|
uprintf("CoCreateInstance for FileOpenDialog failed: %s\n", WindowsErrorString());
|
|
|
|
pfd = NULL; // Just in case
|
|
|
|
goto fallback;
|
|
|
|
}
|
2012-02-02 13:16:49 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
// Set the file extension filters
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileDialog_SetFileTypes(pfd, (UINT)ext->count + 1, filter_spec);
|
2012-02-02 13:16:49 +00:00
|
|
|
|
2018-10-06 12:21:13 +00:00
|
|
|
if (path == NULL) {
|
|
|
|
// Try to use the "Downloads" folder as the initial default directory
|
|
|
|
const GUID download_dir_guid =
|
|
|
|
{ 0x374de290, 0x123f, 0x4565, { 0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b } };
|
|
|
|
hr = SHGetKnownFolderPath(&download_dir_guid, 0, 0, &wpath);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
hr = SHCreateItemFromParsingName(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path);
|
|
|
|
if (SUCCEEDED(hr)) {
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileDialog_SetDefaultFolder(pfd, si_path);
|
2018-10-06 12:21:13 +00:00
|
|
|
}
|
|
|
|
CoTaskMemFree(wpath);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
wpath = utf8_to_wchar(path);
|
|
|
|
hr = SHCreateItemFromParsingName(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path);
|
|
|
|
if (SUCCEEDED(hr)) {
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileDialog_SetFolder(pfd, si_path);
|
2018-10-06 12:21:13 +00:00
|
|
|
}
|
|
|
|
safe_free(wpath);
|
2017-11-13 14:29:48 +00:00
|
|
|
}
|
2012-02-02 13:16:49 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
// Set the default filename
|
|
|
|
wfilename = utf8_to_wchar((ext->filename == NULL) ? "" : ext->filename);
|
|
|
|
if (wfilename != NULL) {
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileDialog_SetFileName(pfd, wfilename);
|
2017-11-13 14:29:48 +00:00
|
|
|
}
|
2012-02-02 13:16:49 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
// Display the dialog
|
2018-11-27 11:59:19 +00:00
|
|
|
hr = IFileDialog_Show(pfd, hMainDialog);
|
2012-02-02 13:16:49 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
// Cleanup
|
|
|
|
safe_free(wfilename);
|
|
|
|
for (i = 0; i < ext->count; i++) {
|
|
|
|
safe_free(filter_spec[i].pszSpec);
|
2014-05-22 00:52:25 +00:00
|
|
|
safe_free(filter_spec[i].pszName);
|
2017-11-13 14:29:48 +00:00
|
|
|
}
|
|
|
|
safe_free(filter_spec[i].pszName);
|
|
|
|
safe_free(filter_spec);
|
2012-02-02 13:16:49 +00:00
|
|
|
|
2017-11-13 14:29:48 +00:00
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
// Obtain the result of the user's interaction with the dialog.
|
2018-11-27 11:59:19 +00:00
|
|
|
hr = IFileDialog_GetResult(pfd, &psiResult);
|
2012-02-02 13:16:49 +00:00
|
|
|
if (SUCCEEDED(hr)) {
|
2018-11-27 11:59:19 +00:00
|
|
|
hr = IShellItem_GetDisplayName(psiResult, SIGDN_FILESYSPATH, &wpath);
|
2012-02-02 13:16:49 +00:00
|
|
|
if (SUCCEEDED(hr)) {
|
2017-11-13 14:29:48 +00:00
|
|
|
filepath = wchar_to_utf8(wpath);
|
|
|
|
CoTaskMemFree(wpath);
|
|
|
|
} else {
|
|
|
|
SetLastError(hr);
|
|
|
|
uprintf("Unable to access file path: %s\n", WindowsErrorString());
|
2012-02-02 13:16:49 +00:00
|
|
|
}
|
2018-11-27 11:59:19 +00:00
|
|
|
IShellItem_Release(psiResult);
|
2012-02-02 13:16:49 +00:00
|
|
|
}
|
2017-11-13 14:29:48 +00:00
|
|
|
} 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;
|
2016-03-23 16:56:49 +00:00
|
|
|
}
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileDialog_Release(pfd);
|
2017-11-13 14:29:48 +00:00
|
|
|
dialog_showing--;
|
|
|
|
return filepath;
|
|
|
|
}
|
|
|
|
|
|
|
|
fallback:
|
|
|
|
safe_free(filter_spec);
|
|
|
|
if (pfd != NULL) {
|
2018-11-27 11:59:19 +00:00
|
|
|
IFileDialog_Release(pfd);
|
2012-02-02 13:16:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ofn, 0, sizeof(ofn));
|
|
|
|
ofn.lStructSize = sizeof(ofn);
|
|
|
|
ofn.hwndOwner = hMainDialog;
|
2014-05-22 00:52:25 +00:00
|
|
|
// Selected File name
|
2015-02-13 00:37:21 +00:00
|
|
|
static_sprintf(selected_name, "%s", (ext->filename == NULL)?"":ext->filename);
|
2012-02-02 13:16:49 +00:00
|
|
|
ofn.lpstrFile = selected_name;
|
|
|
|
ofn.nMaxFile = MAX_PATH;
|
|
|
|
// Set the file extension filters
|
2013-10-15 21:58:27 +00:00
|
|
|
all_files = lmprintf(MSG_107);
|
2014-05-22 00:52:25 +00:00
|
|
|
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);
|
2013-01-20 23:34:13 +00:00
|
|
|
if (ext_string == NULL)
|
|
|
|
return NULL;
|
2014-06-03 21:42:42 +00:00
|
|
|
ext_string[0] = 0;
|
2014-05-22 00:52:25 +00:00
|
|
|
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);
|
2012-02-02 13:16:49 +00:00
|
|
|
// Microsoft could really have picked a better delimiter!
|
|
|
|
for (i=0; i<ext_strlen; i++) {
|
2017-03-10 18:07:48 +00:00
|
|
|
// Since the VS Code Analysis tool is dumb...
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
#pragma warning(suppress: 6385)
|
|
|
|
#endif
|
2012-02-02 13:16:49 +00:00
|
|
|
if (ext_string[i] == '\r') {
|
2017-03-10 18:07:48 +00:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
#pragma warning(suppress: 6386)
|
|
|
|
#endif
|
2012-02-02 13:16:49 +00:00
|
|
|
ext_string[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ofn.lpstrFilter = ext_string;
|
2014-05-22 00:52:25 +00:00
|
|
|
ofn.nFilterIndex = 1;
|
2012-02-02 13:16:49 +00:00
|
|
|
ofn.lpstrInitialDir = path;
|
2014-03-17 20:42:10 +00:00
|
|
|
ofn.Flags = OFN_OVERWRITEPROMPT | options;
|
2012-02-02 13:16:49 +00:00
|
|
|
// Show Dialog
|
|
|
|
if (save) {
|
|
|
|
r = GetSaveFileNameU(&ofn);
|
|
|
|
} else {
|
|
|
|
r = GetOpenFileNameU(&ofn);
|
|
|
|
}
|
|
|
|
if (r) {
|
|
|
|
filepath = safe_strdup(selected_name);
|
|
|
|
} else {
|
|
|
|
tmp = CommDlgExtendedError();
|
|
|
|
if (tmp != 0) {
|
2013-10-15 21:58:27 +00:00
|
|
|
uprintf("Could not select file for %s. Error %X\n", save?"save":"open", tmp);
|
2012-02-02 13:16:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
safe_free(ext_string);
|
2012-12-07 00:54:40 +00:00
|
|
|
dialog_showing--;
|
2012-02-02 13:16:49 +00:00
|
|
|
return filepath;
|
|
|
|
}
|
|
|
|
|
2011-12-08 12:14:21 +00:00
|
|
|
/*
|
|
|
|
* Create the application status bar
|
|
|
|
*/
|
|
|
|
void CreateStatusBar(void)
|
|
|
|
{
|
|
|
|
RECT rect;
|
2018-03-22 23:14:20 +00:00
|
|
|
int edge[2];
|
2015-07-02 21:46:26 +00:00
|
|
|
HFONT hFont;
|
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
// Create the status bar
|
2018-05-11 16:35:48 +00:00
|
|
|
hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | SBARS_TOOLTIPS,
|
2015-07-02 21:46:26 +00:00
|
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hMainDialog,
|
|
|
|
(HMENU)IDC_STATUS, hMainInstance, NULL);
|
2011-12-08 12:14:21 +00:00
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
// Create 2 status areas
|
2011-12-08 12:14:21 +00:00
|
|
|
GetClientRect(hMainDialog, &rect);
|
2018-03-22 23:14:20 +00:00
|
|
|
edge[0] = rect.right - (int)(SB_TIMER_SECTION_SIZE * fScale);
|
|
|
|
edge[1] = rect.right;
|
2015-07-02 21:46:26 +00:00
|
|
|
SendMessage(hStatus, SB_SETPARTS, (WPARAM)ARRAYSIZE(edge), (LPARAM)&edge);
|
2015-06-30 22:22:10 +00:00
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
// Set the font
|
|
|
|
hFont = CreateFontA(-MulDiv(9, GetDeviceCaps(GetDC(hMainDialog), LOGPIXELSY), 72),
|
|
|
|
0, 0, 0, FW_MEDIUM, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
|
|
|
|
0, 0, PROOF_QUALITY, 0, "Segoe UI");
|
|
|
|
SendMessage(hStatus, WM_SETFONT, (WPARAM)hFont, TRUE);
|
2011-12-08 12:14:21 +00:00
|
|
|
}
|
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
/*
|
|
|
|
* Center a dialog with regards to the main application Window or the desktop
|
2019-03-07 16:29:43 +00:00
|
|
|
* See https://docs.microsoft.com/en-gb/windows/desktop/dlgbox/using-dialog-boxes#initializing-a-dialog-box
|
2011-11-21 17:06:17 +00:00
|
|
|
*/
|
2019-03-07 16:29:43 +00:00
|
|
|
void CenterDialog(HWND hDlg, HWND hParent)
|
2011-11-21 17:06:17 +00:00
|
|
|
{
|
2013-10-24 21:57:34 +00:00
|
|
|
RECT rc, rcDlg, rcParent;
|
2011-11-21 17:06:17 +00:00
|
|
|
|
2019-03-07 16:29:43 +00:00
|
|
|
if (hParent == NULL)
|
|
|
|
hParent = GetParent(hDlg);
|
|
|
|
if (hParent == NULL)
|
2011-11-21 17:06:17 +00:00
|
|
|
hParent = GetDesktopWindow();
|
|
|
|
|
2013-10-24 21:57:34 +00:00
|
|
|
GetWindowRect(hParent, &rcParent);
|
|
|
|
GetWindowRect(hDlg, &rcDlg);
|
|
|
|
CopyRect(&rc, &rcParent);
|
2011-11-21 17:06:17 +00:00
|
|
|
|
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);
|
2011-11-21 17:06:17 +00:00
|
|
|
|
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);
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
|
2013-10-15 21:58:27 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2015-08-22 14:18:25 +00:00
|
|
|
void ResizeMoveCtrl(HWND hDlg, HWND hCtrl, int dx, int dy, int dw, int dh, float scale)
|
2013-10-15 21:58:27 +00:00
|
|
|
{
|
|
|
|
RECT rect;
|
|
|
|
POINT point;
|
2014-10-29 18:27:17 +00:00
|
|
|
SIZE border;
|
2013-10-15 21:58:27 +00:00
|
|
|
|
|
|
|
GetWindowRect(hCtrl, &rect);
|
2015-10-19 21:14:08 +00:00
|
|
|
point.x = (right_to_left_mode && (hDlg != hCtrl))?rect.right:rect.left;
|
2013-10-15 21:58:27 +00:00
|
|
|
point.y = rect.top;
|
2015-09-25 23:30:16 +00:00
|
|
|
if (hDlg != hCtrl)
|
|
|
|
ScreenToClient(hDlg, &point);
|
2013-10-15 21:58:27 +00:00
|
|
|
GetClientRect(hCtrl, &rect);
|
|
|
|
|
2014-10-29 18:27:17 +00:00
|
|
|
// If the control has any borders (dialog, edit box), take them into account
|
|
|
|
border = GetBorderSize(hCtrl);
|
2015-08-22 14:18:25 +00:00
|
|
|
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);
|
2018-05-08 19:28:23 +00:00
|
|
|
// Don't be tempted to call InvalidateRect() here - it causes intempestive whole screen refreshes
|
2013-10-15 21:58:27 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
void ResizeButtonHeight(HWND hDlg, int id)
|
|
|
|
{
|
2018-05-11 16:35:48 +00:00
|
|
|
HWND hCtrl, hPrevCtrl;
|
2018-03-22 23:14:20 +00:00
|
|
|
RECT rc;
|
|
|
|
int dy = 0;
|
|
|
|
|
|
|
|
hCtrl = GetDlgItem(hDlg, id);
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
|
|
|
if (rc.bottom - rc.top < bh)
|
|
|
|
dy = (bh - (rc.bottom - rc.top)) / 2;
|
2018-05-11 16:35:48 +00:00
|
|
|
hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
|
|
|
|
SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top - dy, rc.right - rc.left, bh, 0);
|
2018-03-22 23:14:20 +00:00
|
|
|
}
|
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
/*
|
|
|
|
* License callback
|
|
|
|
*/
|
|
|
|
INT_PTR CALLBACK LicenseCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
2018-08-20 08:46:23 +00:00
|
|
|
LONG_PTR style;
|
2016-12-05 10:52:37 +00:00
|
|
|
HWND hLicense;
|
2011-11-21 17:06:17 +00:00
|
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG:
|
2016-12-05 10:52:37 +00:00
|
|
|
hLicense = GetDlgItem(hDlg, IDC_LICENSE_TEXT);
|
2013-10-15 21:58:27 +00:00
|
|
|
apply_localization(IDD_LICENSE, hDlg);
|
2019-03-07 16:29:43 +00:00
|
|
|
CenterDialog(hDlg, NULL);
|
2018-03-22 23:14:20 +00:00
|
|
|
ResizeButtonHeight(hDlg, IDCANCEL);
|
2016-12-05 10:52:37 +00:00
|
|
|
// Suppress any inherited RTL flags
|
2018-08-20 08:46:23 +00:00
|
|
|
style = GetWindowLongPtr(hLicense, GWL_EXSTYLE);
|
2016-12-05 10:52:37 +00:00
|
|
|
style &= ~(WS_EX_RTLREADING | WS_EX_RIGHT | WS_EX_LEFTSCROLLBAR);
|
2018-08-20 08:46:23 +00:00
|
|
|
SetWindowLongPtr(hLicense, GWL_EXSTYLE, style);
|
|
|
|
style = GetWindowLongPtr(hLicense, GWL_STYLE);
|
2016-12-05 10:52:37 +00:00
|
|
|
style &= ~(ES_RIGHT);
|
2018-08-20 08:46:23 +00:00
|
|
|
SetWindowLongPtr(hLicense, GWL_STYLE, style);
|
2011-11-21 17:06:17 +00:00
|
|
|
SetDlgItemTextA(hDlg, IDC_LICENSE_TEXT, gplv3);
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDOK:
|
|
|
|
case IDCANCEL:
|
2013-10-15 21:58:27 +00:00
|
|
|
reset_localization(IDD_LICENSE);
|
2011-11-21 17:06:17 +00:00
|
|
|
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)
|
|
|
|
{
|
2015-09-25 23:30:16 +00:00
|
|
|
int i, dy;
|
2012-11-29 02:59:52 +00:00
|
|
|
const int edit_id[2] = {IDC_ABOUT_BLURB, IDC_ABOUT_COPYRIGHTS};
|
2013-10-15 21:58:27 +00:00
|
|
|
char about_blurb[2048];
|
2012-11-29 02:59:52 +00:00
|
|
|
const char* edit_text[2] = {about_blurb, additional_copyrights};
|
2018-03-22 23:14:20 +00:00
|
|
|
HWND hEdit[2], hCtrl;
|
2012-11-08 20:36:48 +00:00
|
|
|
TEXTRANGEW tr;
|
|
|
|
ENLINK* enl;
|
2018-03-22 23:14:20 +00:00
|
|
|
RECT rc;
|
2015-09-25 23:30:16 +00:00
|
|
|
REQRESIZE* rsz;
|
2012-11-08 20:36:48 +00:00
|
|
|
wchar_t wUrl[256];
|
2015-10-18 20:56:24 +00:00
|
|
|
static BOOL resized_already = TRUE;
|
2012-11-08 20:36:48 +00:00
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG:
|
2015-10-18 20:56:24 +00:00
|
|
|
resized_already = FALSE;
|
2013-10-15 21:58:27 +00:00
|
|
|
// Execute dialog localization
|
|
|
|
apply_localization(IDD_ABOUTBOX, hDlg);
|
2012-12-05 01:53:10 +00:00
|
|
|
SetTitleBarIcon(hDlg);
|
2019-03-07 16:29:43 +00:00
|
|
|
CenterDialog(hDlg, NULL);
|
2018-03-22 23:14:20 +00:00
|
|
|
// Resize the 'License' button
|
|
|
|
hCtrl = GetDlgItem(hDlg, IDC_ABOUT_LICENSE);
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
|
|
|
dy = 0;
|
|
|
|
if (rc.bottom - rc.top < bh)
|
|
|
|
dy = (bh - (rc.bottom - rc.top)) / 2;
|
|
|
|
SetWindowPos(hCtrl, NULL, rc.left, rc.top - dy,
|
|
|
|
max(rc.right - rc.left, GetTextSize(hCtrl, NULL).cx + cbw), bh, SWP_NOZORDER);
|
|
|
|
ResizeButtonHeight(hDlg, IDOK);
|
2017-08-10 18:43:04 +00:00
|
|
|
static_sprintf(about_blurb, about_blurb_format, lmprintf(MSG_174|MSG_RTF),
|
2015-10-21 23:40:24 +00:00
|
|
|
lmprintf(MSG_175|MSG_RTF, rufus_version[0], rufus_version[1], rufus_version[2]),
|
2019-01-02 11:41:23 +00:00
|
|
|
"Copyright © 2011-2019 Pete Batard / Akeo",
|
2015-10-21 23:40:24 +00:00
|
|
|
lmprintf(MSG_176|MSG_RTF), lmprintf(MSG_177|MSG_RTF), lmprintf(MSG_178|MSG_RTF));
|
2012-11-29 02:59:52 +00:00
|
|
|
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);
|
2015-09-25 23:30:16 +00:00
|
|
|
SendMessage(hEdit[i], EM_SETEVENTMASK, 0, ENM_LINK|((i==0)?ENM_REQUESTRESIZE:0));
|
2012-11-29 02:59:52 +00:00
|
|
|
SendMessage(hEdit[i], EM_SETBKGNDCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNFACE));
|
|
|
|
}
|
2015-05-08 22:37:22 +00:00
|
|
|
// 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);
|
2015-09-25 23:30:16 +00:00
|
|
|
SendMessage(hEdit[0], EM_REQUESTRESIZE, 0, 0);
|
2011-11-21 17:06:17 +00:00
|
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
|
|
switch (((LPNMHDR)lParam)->code) {
|
2015-09-25 23:30:16 +00:00
|
|
|
case EN_REQUESTRESIZE:
|
2015-10-18 20:56:24 +00:00
|
|
|
if (!resized_already) {
|
|
|
|
resized_already = TRUE;
|
2018-03-22 23:14:20 +00:00
|
|
|
GetWindowRect(GetDlgItem(hDlg, edit_id[0]), &rc);
|
|
|
|
dy = rc.bottom - rc.top;
|
2015-10-18 20:56:24 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-09-25 23:30:16 +00:00
|
|
|
break;
|
2012-11-08 20:36:48 +00:00
|
|
|
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);
|
2013-01-09 21:54:28 +00:00
|
|
|
wUrl[ARRAYSIZE(wUrl)-1] = 0;
|
2012-11-08 20:36:48 +00:00
|
|
|
ShellExecuteW(hDlg, L"open", wUrl, NULL, NULL, SW_SHOWNORMAL);
|
|
|
|
}
|
|
|
|
break;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDOK:
|
|
|
|
case IDCANCEL:
|
2013-10-15 21:58:27 +00:00
|
|
|
reset_localization(IDD_ABOUTBOX);
|
2011-11-21 17:06:17 +00:00
|
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
|
|
return (INT_PTR)TRUE;
|
|
|
|
case IDC_ABOUT_LICENSE:
|
2015-07-03 22:42:45 +00:00
|
|
|
MyDialogBox(hMainInstance, IDD_LICENSE, hDlg, LicenseCallback);
|
2011-11-21 17:06:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (INT_PTR)FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
INT_PTR CreateAboutBox(void)
|
|
|
|
{
|
2012-12-07 00:54:40 +00:00
|
|
|
INT_PTR r;
|
|
|
|
dialog_showing++;
|
2015-07-03 22:42:45 +00:00
|
|
|
r = MyDialogBox(hMainInstance, IDD_ABOUTBOX, hMainDialog, AboutCallback);
|
2012-12-07 00:54:40 +00:00
|
|
|
dialog_showing--;
|
|
|
|
return r;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
2018-11-20 12:28:20 +00:00
|
|
|
int i, dh, cbh = 0;
|
2011-11-21 17:06:17 +00:00
|
|
|
// Prevent resizing
|
|
|
|
static LRESULT disabled[9] = { HTLEFT, HTRIGHT, HTTOP, HTBOTTOM, HTSIZE,
|
|
|
|
HTTOPLEFT, HTTOPRIGHT, HTBOTTOMLEFT, HTBOTTOMRIGHT };
|
2018-11-20 12:28:20 +00:00
|
|
|
static HBRUSH background_brush, separator_brush, buttonface_brush;
|
2014-01-03 01:52:20 +00:00
|
|
|
// To use the system message font
|
|
|
|
NONCLIENTMETRICS ncm;
|
|
|
|
HFONT hDlgFont;
|
2018-03-22 23:14:20 +00:00
|
|
|
HWND hCtrl;
|
|
|
|
RECT rc;
|
2018-06-08 12:02:39 +00:00
|
|
|
HDC hDC;
|
2011-11-21 17:06:17 +00:00
|
|
|
|
|
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG:
|
2017-11-13 14:29:48 +00:00
|
|
|
// 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.
|
2018-11-20 12:28:20 +00:00
|
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
|
2017-11-13 14:29:48 +00:00
|
|
|
ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth);
|
2018-11-20 12:28:20 +00:00
|
|
|
#endif
|
2017-11-13 14:29:48 +00:00
|
|
|
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));
|
2018-03-22 23:14:20 +00:00
|
|
|
if (bh != 0) {
|
|
|
|
ResizeButtonHeight(hDlg, IDC_MORE_INFO);
|
|
|
|
ResizeButtonHeight(hDlg, IDYES);
|
|
|
|
ResizeButtonHeight(hDlg, IDNO);
|
|
|
|
}
|
2014-01-03 01:52:20 +00:00
|
|
|
|
2013-10-15 21:58:27 +00:00
|
|
|
apply_localization(IDD_NOTIFICATION, hDlg);
|
2015-09-20 23:20:19 +00:00
|
|
|
background_brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
|
2015-06-21 16:59:59 +00:00
|
|
|
separator_brush = CreateSolidBrush(GetSysColor(COLOR_3DLIGHT));
|
2018-11-20 12:28:20 +00:00
|
|
|
buttonface_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
|
2012-12-05 01:53:10 +00:00
|
|
|
SetTitleBarIcon(hDlg);
|
2019-03-07 16:29:43 +00:00
|
|
|
CenterDialog(hDlg, NULL);
|
2011-11-21 17:06:17 +00:00
|
|
|
// 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");
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
// Set the dialog title
|
|
|
|
if (szMessageTitle != NULL) {
|
2013-10-15 21:58:27 +00:00
|
|
|
SetWindowTextU(hDlg, szMessageTitle);
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
2012-11-29 23:14:36 +00:00
|
|
|
// Enable/disable the buttons and set text
|
|
|
|
if (!notification_is_question) {
|
2013-10-15 21:58:27 +00:00
|
|
|
SetWindowTextU(GetDlgItem(hDlg, IDNO), lmprintf(MSG_006));
|
2012-11-29 23:14:36 +00:00
|
|
|
} else {
|
|
|
|
ShowWindow(GetDlgItem(hDlg, IDYES), SW_SHOW);
|
|
|
|
}
|
2018-11-20 12:28:20 +00:00
|
|
|
hCtrl = GetDlgItem(hDlg, IDC_DONT_DISPLAY_AGAIN);
|
|
|
|
if (notification_dont_display_setting != NULL) {
|
|
|
|
SetWindowTextU(hCtrl, lmprintf(MSG_127));
|
|
|
|
} else {
|
|
|
|
// Remove the "Don't display again" checkbox
|
|
|
|
ShowWindow(hCtrl, SW_HIDE);
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
|
|
|
cbh = rc.bottom - rc.top;
|
|
|
|
}
|
2012-12-02 03:50:08 +00:00
|
|
|
if ((notification_more_info != NULL) && (notification_more_info->callback != NULL)) {
|
2018-03-22 23:14:20 +00:00
|
|
|
hCtrl = GetDlgItem(hDlg, IDC_MORE_INFO);
|
|
|
|
// Resize the 'More information' button
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
|
|
|
SetWindowPos(hCtrl, NULL, rc.left, rc.top,
|
|
|
|
max(rc.right - rc.left, GetTextSize(hCtrl, NULL).cx + cbw), rc.bottom - rc.top, SWP_NOZORDER);
|
|
|
|
ShowWindow(hCtrl, SW_SHOW);
|
2012-11-29 23:14:36 +00:00
|
|
|
}
|
2018-06-08 12:02:39 +00:00
|
|
|
// Set the control text and resize the dialog if needed
|
2011-11-21 17:06:17 +00:00
|
|
|
if (szMessageText != NULL) {
|
2018-06-08 12:02:39 +00:00
|
|
|
hCtrl = GetDlgItem(hDlg, IDC_NOTIFICATION_TEXT);
|
|
|
|
SetWindowTextU(hCtrl, szMessageText);
|
|
|
|
hDC = GetDC(hCtrl);
|
|
|
|
SelectFont(hDC, hDlgFont); // Yes, you *MUST* reapply the font to the DC, even after SetWindowText!
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
dh = rc.bottom - rc.top;
|
|
|
|
DrawTextU(hDC, szMessageText, -1, &rc, DT_CALCRECT | DT_WORDBREAK);
|
2018-11-20 12:28:20 +00:00
|
|
|
dh = max(rc.bottom - rc.top - dh + (int)(8.0f * fScale), 0);
|
2018-06-08 12:02:39 +00:00
|
|
|
safe_release_dc(hCtrl, hDC);
|
2018-11-20 12:28:20 +00:00
|
|
|
ResizeMoveCtrl(hDlg, hCtrl, 0, 0, 0, dh, 1.0f);
|
|
|
|
ResizeMoveCtrl(hDlg, hDlg, 0, 0, 0, dh - cbh, 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, IDC_DONT_DISPLAY_AGAIN), 0, dh, 0, 0, 1.0f);
|
|
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_MORE_INFO), 0, dh - cbh, 0, 0, 1.0f);
|
|
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDYES), 0, dh -cbh, 0, 0, 1.0f);
|
|
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDNO), 0, dh -cbh, 0, 0, 1.0f);
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
2018-11-20 12:28:20 +00:00
|
|
|
if ((HWND)lParam == GetDlgItem(hDlg, IDC_DONT_DISPLAY_AGAIN)) {
|
|
|
|
return (INT_PTR)buttonface_brush;
|
|
|
|
}
|
2015-06-21 16:59:59 +00:00
|
|
|
return (INT_PTR)background_brush;
|
2011-11-21 17:06:17 +00:00
|
|
|
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:
|
2012-11-29 23:14:36 +00:00
|
|
|
case IDYES:
|
|
|
|
case IDNO:
|
2018-11-20 12:28:20 +00:00
|
|
|
if (IsDlgButtonChecked(hDlg, IDC_DONT_DISPLAY_AGAIN) == BST_CHECKED) {
|
|
|
|
WriteSettingBool(SETTING_DISABLE_SECURE_BOOT_NOTICE, TRUE);
|
|
|
|
}
|
2011-11-21 17:06:17 +00:00
|
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
|
|
return (INT_PTR)TRUE;
|
2012-11-29 23:14:36 +00:00
|
|
|
case IDC_MORE_INFO:
|
2018-11-20 12:28:20 +00:00
|
|
|
assert(notification_more_info->callback != NULL);
|
|
|
|
if (notification_more_info != NULL) {
|
|
|
|
if (notification_more_info->id == MORE_INFO_URL) {
|
|
|
|
ShellExecuteA(hDlg, "open", notification_more_info->url, NULL, NULL, SW_SHOWNORMAL);
|
|
|
|
} else {
|
|
|
|
MyDialogBox(hMainInstance, notification_more_info->id, hDlg, notification_more_info->callback);
|
|
|
|
}
|
|
|
|
}
|
2012-11-29 23:14:36 +00:00
|
|
|
break;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (INT_PTR)FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Display a custom notification
|
|
|
|
*/
|
2018-11-20 12:28:20 +00:00
|
|
|
BOOL Notification(int type, const char* dont_display_setting, const notification_info* more_info, char* title, char* format, ...)
|
2011-11-21 17:06:17 +00:00
|
|
|
{
|
2012-11-29 23:14:36 +00:00
|
|
|
BOOL ret;
|
2011-12-04 19:47:27 +00:00
|
|
|
va_list args;
|
2018-06-08 15:29:41 +00:00
|
|
|
const int max_msg_size = 1024;
|
2012-12-07 00:54:40 +00:00
|
|
|
|
|
|
|
dialog_showing++;
|
2018-06-08 15:29:41 +00:00
|
|
|
szMessageText = (char*)malloc(max_msg_size);
|
|
|
|
if (szMessageText == NULL)
|
|
|
|
return FALSE;
|
2017-06-24 16:23:06 +00:00
|
|
|
szMessageTitle = safe_strdup(title);
|
2018-06-08 15:29:41 +00:00
|
|
|
if (szMessageTitle == NULL)
|
|
|
|
return FALSE;
|
2011-12-04 19:47:27 +00:00
|
|
|
va_start(args, format);
|
2018-06-08 15:29:41 +00:00
|
|
|
safe_vsnprintf(szMessageText, max_msg_size -1, format, args);
|
2011-12-04 19:47:27 +00:00
|
|
|
va_end(args);
|
2018-06-08 15:29:41 +00:00
|
|
|
szMessageText[max_msg_size -1] = 0;
|
2012-12-02 03:50:08 +00:00
|
|
|
notification_more_info = more_info;
|
2012-11-29 23:14:36 +00:00
|
|
|
notification_is_question = FALSE;
|
2018-11-20 12:28:20 +00:00
|
|
|
notification_dont_display_setting = dont_display_setting;
|
2011-12-04 19:47:27 +00:00
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
switch(type) {
|
2017-07-16 21:42:19 +00:00
|
|
|
case MSG_WARNING_QUESTION:
|
|
|
|
notification_is_question = TRUE;
|
|
|
|
// Fall through
|
2011-11-21 17:06:17 +00:00
|
|
|
case MSG_WARNING:
|
|
|
|
hMessageIcon = LoadIcon(NULL, IDI_WARNING);
|
|
|
|
break;
|
|
|
|
case MSG_ERROR:
|
|
|
|
hMessageIcon = LoadIcon(NULL, IDI_ERROR);
|
|
|
|
break;
|
2012-11-29 23:14:36 +00:00
|
|
|
case MSG_QUESTION:
|
|
|
|
hMessageIcon = LoadIcon(NULL, IDI_QUESTION);
|
|
|
|
notification_is_question = TRUE;
|
|
|
|
break;
|
2011-11-21 17:06:17 +00:00
|
|
|
case MSG_INFO:
|
|
|
|
default:
|
|
|
|
hMessageIcon = LoadIcon(NULL, IDI_INFORMATION);
|
|
|
|
break;
|
|
|
|
}
|
2015-07-03 22:42:45 +00:00
|
|
|
ret = (MyDialogBox(hMainInstance, IDD_NOTIFICATION, hMainDialog, NotificationCallback) == IDYES);
|
2012-11-29 02:59:52 +00:00
|
|
|
safe_free(szMessageText);
|
2017-06-24 16:23:06 +00:00
|
|
|
safe_free(szMessageTitle);
|
2012-12-07 00:54:40 +00:00
|
|
|
dialog_showing--;
|
2012-11-29 02:59:52 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-08-22 14:18:25 +00:00
|
|
|
/*
|
2017-07-11 16:43:51 +00:00
|
|
|
* Custom dialog for radio button selection dialog
|
|
|
|
*/
|
2015-08-22 14:18:25 +00:00
|
|
|
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;
|
2018-06-08 12:02:39 +00:00
|
|
|
RECT rc, rc2;
|
2015-08-22 14:18:25 +00:00
|
|
|
HFONT hDlgFont;
|
|
|
|
HWND hCtrl;
|
2015-08-22 14:57:23 +00:00
|
|
|
HDC hDC;
|
2015-08-22 14:18:25 +00:00
|
|
|
|
|
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG:
|
2016-12-13 14:21:51 +00:00
|
|
|
// Don't overflow our max radio button
|
2017-07-11 16:43:51 +00:00
|
|
|
if (nDialogItems > (IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1 + 1)) {
|
2017-01-18 13:46:16 +00:00
|
|
|
uprintf("Warning: Too many options requested for Selection (%d vs %d)",
|
2017-07-11 16:43:51 +00:00
|
|
|
nDialogItems, IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1);
|
|
|
|
nDialogItems = IDC_SELECTION_CHOICEMAX - IDC_SELECTION_CHOICE1;
|
2016-12-13 14:21:51 +00:00
|
|
|
}
|
2015-08-22 14:18:25 +00:00
|
|
|
// Get the system message box font. See http://stackoverflow.com/a/6057761
|
|
|
|
ncm.cbSize = sizeof(ncm);
|
|
|
|
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));
|
2017-07-11 16:43:51 +00:00
|
|
|
for (i = 0; i < nDialogItems; i++)
|
2016-12-13 14:21:51 +00:00
|
|
|
SendMessage(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), WM_SETFONT, (WPARAM)hDlgFont, MAKELPARAM(TRUE, 0));
|
2015-08-22 14:18:25 +00:00
|
|
|
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);
|
2015-09-20 23:20:19 +00:00
|
|
|
background_brush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
|
2015-08-22 14:18:25 +00:00
|
|
|
separator_brush = CreateSolidBrush(GetSysColor(COLOR_3DLIGHT));
|
|
|
|
SetTitleBarIcon(hDlg);
|
2019-03-07 16:29:43 +00:00
|
|
|
CenterDialog(hDlg, NULL);
|
2015-08-22 14:18:25 +00:00
|
|
|
// 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);
|
2017-07-11 16:43:51 +00:00
|
|
|
for (i = 0; i < nDialogItems; i++) {
|
|
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), szDialogItem[i]);
|
2016-12-13 14:21:51 +00:00
|
|
|
ShowWindow(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), SW_SHOW);
|
|
|
|
}
|
2015-08-22 14:18:25 +00:00
|
|
|
// Move/Resize the controls as needed to fit our text
|
|
|
|
hCtrl = GetDlgItem(hDlg, IDC_SELECTION_TEXT);
|
2015-08-22 14:57:23 +00:00
|
|
|
hDC = GetDC(hCtrl);
|
|
|
|
SelectFont(hDC, hDlgFont); // Yes, you *MUST* reapply the font to the DC, even after SetWindowText!
|
2018-06-08 12:02:39 +00:00
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
dh = rc.bottom - rc.top;
|
|
|
|
DrawTextU(hDC, szMessageText, -1, &rc, DT_CALCRECT | DT_WORDBREAK);
|
|
|
|
dh = rc.bottom - rc.top - dh;
|
2018-03-22 23:14:20 +00:00
|
|
|
safe_release_dc(hCtrl, hDC);
|
2015-08-22 14:18:25 +00:00
|
|
|
ResizeMoveCtrl(hDlg, hCtrl, 0, 0, 0, dh, 1.0f);
|
2017-07-11 16:43:51 +00:00
|
|
|
for (i = 0; i < nDialogItems; i++)
|
2016-12-13 14:21:51 +00:00
|
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i), 0, dh, 0, 0, 1.0f);
|
2017-07-11 16:43:51 +00:00
|
|
|
if (nDialogItems > 2) {
|
2018-06-08 12:02:39 +00:00
|
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_SELECTION_CHOICE2), &rc);
|
|
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + nDialogItems - 1), &rc2);
|
|
|
|
dh += rc2.top - rc.top;
|
2016-12-13 14:21:51 +00:00
|
|
|
}
|
2015-08-22 14:18:25 +00:00
|
|
|
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);
|
2018-03-22 23:14:20 +00:00
|
|
|
ResizeButtonHeight(hDlg, IDOK);
|
|
|
|
ResizeButtonHeight(hDlg, IDCANCEL);
|
2015-08-22 14:18:25 +00:00
|
|
|
|
|
|
|
// 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:
|
2017-07-11 16:43:51 +00:00
|
|
|
for (i = 0; (i < nDialogItems) &&
|
2016-12-13 14:21:51 +00:00
|
|
|
(Button_GetCheck(GetDlgItem(hDlg, IDC_SELECTION_CHOICE1 + i)) != BST_CHECKED); i++);
|
2017-07-11 16:43:51 +00:00
|
|
|
if (i < nDialogItems)
|
2016-12-13 14:21:51 +00:00
|
|
|
r = i + 1;
|
2015-08-22 14:18:25 +00:00
|
|
|
// Fall through
|
|
|
|
case IDNO:
|
|
|
|
case IDCANCEL:
|
|
|
|
EndDialog(hDlg, r);
|
|
|
|
return (INT_PTR)TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (INT_PTR)FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-07-11 16:43:51 +00:00
|
|
|
* Display an item selection dialog
|
|
|
|
*/
|
|
|
|
int SelectionDialog(char* title, char* message, char** choices, int size)
|
2015-08-22 14:18:25 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
dialog_showing++;
|
|
|
|
szMessageTitle = title;
|
|
|
|
szMessageText = message;
|
2017-07-11 16:43:51 +00:00
|
|
|
szDialogItem = choices;
|
|
|
|
nDialogItems = size;
|
2015-08-22 14:18:25 +00:00
|
|
|
ret = (int)MyDialogBox(hMainInstance, IDD_SELECTION, hMainDialog, SelectionCallback);
|
|
|
|
dialog_showing--;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-11 16:43:51 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2018-06-08 12:02:39 +00:00
|
|
|
RECT rc, rc2;
|
2017-07-11 16:43:51 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
// Get the system message box font. See http://stackoverflow.com/a/6057761
|
|
|
|
ncm.cbSize = sizeof(ncm);
|
|
|
|
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);
|
2019-03-07 16:29:43 +00:00
|
|
|
CenterDialog(hDlg, NULL);
|
2017-07-11 16:43:51 +00:00
|
|
|
// 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!
|
2018-06-08 12:02:39 +00:00
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
dh = rc.bottom - rc.top;
|
|
|
|
DrawTextU(hDC, szMessageText, -1, &rc, DT_CALCRECT | DT_WORDBREAK);
|
|
|
|
dh = rc.bottom - rc.top - dh;
|
2018-03-22 23:14:20 +00:00
|
|
|
safe_release_dc(hCtrl, hDC);
|
2017-07-11 16:43:51 +00:00
|
|
|
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) {
|
2018-06-08 12:02:39 +00:00
|
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_LIST_ITEM1), &rc);
|
|
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_LIST_ITEM1 + nDialogItems - 1), &rc2);
|
|
|
|
dh += rc2.top - rc.top;
|
2017-07-11 16:43:51 +00:00
|
|
|
}
|
|
|
|
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);
|
2018-03-22 23:14:20 +00:00
|
|
|
ResizeButtonHeight(hDlg, IDOK);
|
|
|
|
ResizeButtonHeight(hDlg, IDCANCEL);
|
2017-07-11 16:43:51 +00:00
|
|
|
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--;
|
|
|
|
}
|
|
|
|
|
2012-02-02 13:16:49 +00:00
|
|
|
static struct {
|
2012-05-21 10:11:42 +00:00
|
|
|
HWND hTip; // Tooltip handle
|
|
|
|
HWND hCtrl; // Handle of the control the tooltip belongs to
|
2011-11-21 17:06:17 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-05-30 16:32:49 +00:00
|
|
|
if (i == MAX_TOOLTIPS)
|
2011-11-21 17:06:17 +00:00
|
|
|
return (INT_PTR)FALSE;
|
|
|
|
|
2016-05-30 16:32:49 +00:00
|
|
|
switch (message) {
|
2011-11-21 17:06:17 +00:00
|
|
|
case WM_NOTIFY:
|
|
|
|
switch (((LPNMHDR)lParam)->code) {
|
|
|
|
case TTN_GETDISPINFOW:
|
|
|
|
lpnmtdi = (LPNMTTDISPINFOW)lParam;
|
|
|
|
lpnmtdi->lpszText = ttlist[i].wstring;
|
2018-05-17 19:45:32 +00:00
|
|
|
// Don't ask me WHY we need to clear RTLREADING for RTL multiline text to look good
|
|
|
|
lpnmtdi->uFlags &= ~TTF_RTLREADING;
|
|
|
|
SendMessage(hDlg, TTM_SETMAXTIPWIDTH, 0, (LPARAM)(int)(150.0f * fScale));
|
2011-11-21 17:06:17 +00:00
|
|
|
return (INT_PTR)TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-05-30 16:32:49 +00:00
|
|
|
#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
|
2011-11-21 17:06:17 +00:00
|
|
|
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
|
|
|
|
*/
|
2012-05-21 10:11:42 +00:00
|
|
|
BOOL CreateTooltip(HWND hControl, const char* message, int duration)
|
2011-11-21 17:06:17 +00:00
|
|
|
{
|
|
|
|
TOOLINFOW toolInfo = {0};
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ( (hControl == NULL) || (message == NULL) ) {
|
2012-05-21 10:11:42 +00:00
|
|
|
return FALSE;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
|
2012-05-21 10:11:42 +00:00
|
|
|
// Destroy existing tooltip if any
|
|
|
|
DestroyTooltip(hControl);
|
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
// 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);
|
2012-05-21 10:11:42 +00:00
|
|
|
return FALSE;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the tooltip window
|
2018-05-11 16:35:48 +00:00
|
|
|
ttlist[i].hTip = CreateWindowEx(right_to_left_mode ? WS_EX_LAYOUTRTL : 0,
|
2018-05-10 11:07:21 +00:00
|
|
|
TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
|
2011-11-21 17:06:17 +00:00
|
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hMainDialog, NULL,
|
|
|
|
hMainInstance, NULL);
|
|
|
|
|
|
|
|
if (ttlist[i].hTip == NULL) {
|
2012-05-21 10:11:42 +00:00
|
|
|
return FALSE;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
2012-05-21 10:11:42 +00:00
|
|
|
ttlist[i].hCtrl = hControl;
|
2011-11-21 17:06:17 +00:00
|
|
|
|
|
|
|
// 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
|
2014-05-15 22:51:33 +00:00
|
|
|
toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS | ((right_to_left_mode)?TTF_RTLREADING:0);
|
2015-07-02 21:46:26 +00:00
|
|
|
// 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;
|
|
|
|
|
2011-11-21 17:06:17 +00:00
|
|
|
toolInfo.uId = (UINT_PTR)hControl;
|
|
|
|
toolInfo.lpszText = LPSTR_TEXTCALLBACKW;
|
|
|
|
SendMessageW(ttlist[i].hTip, TTM_ADDTOOLW, 0, (LPARAM)&toolInfo);
|
|
|
|
|
2012-05-21 10:11:42 +00:00
|
|
|
return TRUE;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
|
2012-05-21 10:11:42 +00:00
|
|
|
/* Destroy a tooltip. hCtrl = handle of the control the tooltip is associated with */
|
|
|
|
void DestroyTooltip(HWND hControl)
|
2011-11-21 17:06:17 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2012-05-21 10:11:42 +00:00
|
|
|
if (hControl == NULL) return;
|
2011-11-21 17:06:17 +00:00
|
|
|
for (i=0; i<MAX_TOOLTIPS; i++) {
|
2012-05-21 10:11:42 +00:00
|
|
|
if (ttlist[i].hCtrl == hControl) break;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
2013-10-24 21:57:34 +00:00
|
|
|
if (i >= MAX_TOOLTIPS) return;
|
2012-05-21 10:11:42 +00:00
|
|
|
DestroyWindow(ttlist[i].hTip);
|
2011-11-21 17:06:17 +00:00
|
|
|
safe_free(ttlist[i].wstring);
|
|
|
|
ttlist[i].original_proc = NULL;
|
|
|
|
ttlist[i].hTip = NULL;
|
2012-05-21 10:11:42 +00:00
|
|
|
ttlist[i].hCtrl = NULL;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DestroyAllTooltips(void)
|
|
|
|
{
|
2013-10-24 21:57:34 +00:00
|
|
|
int i, j;
|
2011-11-21 17:06:17 +00:00
|
|
|
|
2013-10-24 21:57:34 +00:00
|
|
|
for (i=0, j=0; i<MAX_TOOLTIPS; i++) {
|
2011-11-21 17:06:17 +00:00
|
|
|
if (ttlist[i].hTip == NULL) continue;
|
2013-10-24 21:57:34 +00:00
|
|
|
j++;
|
2011-11-21 17:06:17 +00:00
|
|
|
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;
|
2011-11-21 17:06:17 +00:00
|
|
|
}
|
|
|
|
}
|
2012-03-01 19:19:12 +00:00
|
|
|
|
2012-05-31 12:05:12 +00:00
|
|
|
/* Determine if a Windows is being displayed or not */
|
|
|
|
BOOL IsShown(HWND hDlg)
|
|
|
|
{
|
2013-01-09 21:54:28 +00:00
|
|
|
WINDOWPLACEMENT placement = {0};
|
|
|
|
placement.length = sizeof(WINDOWPLACEMENT);
|
2012-05-31 12:05:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-22 11:30:22 +00:00
|
|
|
/* Compute the width of a dropdown list entry */
|
2012-03-01 19:19:12 +00:00
|
|
|
LONG GetEntryWidth(HWND hDropDown, const char *entry)
|
2015-10-18 20:31:47 +00:00
|
|
|
{
|
2012-03-01 19:19:12 +00:00
|
|
|
HDC hDC;
|
2012-11-03 17:22:28 +00:00
|
|
|
HFONT hFont, hDefFont = NULL;
|
2012-03-01 19:19:12 +00:00
|
|
|
SIZE size;
|
|
|
|
|
|
|
|
hDC = GetDC(hDropDown);
|
|
|
|
hFont = (HFONT)SendMessage(hDropDown, WM_GETFONT, 0, 0);
|
|
|
|
if (hFont != NULL)
|
|
|
|
hDefFont = (HFONT)SelectObject(hDC, hFont);
|
2015-10-18 20:31:47 +00:00
|
|
|
|
2014-12-03 18:44:00 +00:00
|
|
|
if (!GetTextExtentPointU(hDC, entry, &size))
|
|
|
|
size.cx = 0;
|
2012-03-01 19:19:12 +00:00
|
|
|
|
|
|
|
if (hFont != NULL)
|
|
|
|
SelectObject(hDC, hDefFont);
|
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
safe_release_dc(hDropDown, hDC);
|
2012-03-01 19:19:12 +00:00
|
|
|
return size.cx;
|
|
|
|
}
|
2012-03-29 17:16:06 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Windows 7 taskbar icon handling (progress bar overlay, etc)
|
|
|
|
*/
|
2017-11-13 14:29:48 +00:00
|
|
|
static ITaskbarList3* ptbl = NULL;
|
2012-03-29 17:16:06 +00:00
|
|
|
|
2012-06-04 16:35:32 +00:00
|
|
|
// Create a taskbar icon progressbar
|
2012-03-29 17:16:06 +00:00
|
|
|
BOOL CreateTaskbarList(void)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
2017-11-13 14:29:48 +00:00
|
|
|
|
|
|
|
hr = CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_ALL, &IID_ITaskbarList3, (LPVOID)&ptbl);
|
2012-03-29 17:16:06 +00:00
|
|
|
if (FAILED(hr)) {
|
2012-05-30 23:32:25 +00:00
|
|
|
uprintf("CoCreateInstance for TaskbarList failed: error %X\n", hr);
|
2012-03-29 17:16:06 +00:00
|
|
|
ptbl = NULL;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL SetTaskbarProgressState(TASKBAR_PROGRESS_FLAGS tbpFlags)
|
|
|
|
{
|
|
|
|
if (ptbl == NULL)
|
|
|
|
return FALSE;
|
2018-11-27 11:59:19 +00:00
|
|
|
return !FAILED(ITaskbarList3_SetProgressState(ptbl, hMainDialog, tbpFlags));
|
2012-03-29 17:16:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BOOL SetTaskbarProgressValue(ULONGLONG ullCompleted, ULONGLONG ullTotal)
|
|
|
|
{
|
|
|
|
if (ptbl == NULL)
|
|
|
|
return FALSE;
|
2018-11-27 11:59:19 +00:00
|
|
|
return !FAILED(ITaskbarList3_SetProgressValue(ptbl, hMainDialog, ullCompleted, ullTotal));
|
2012-03-29 17:16:06 +00:00
|
|
|
}
|
2012-11-29 23:14:36 +00:00
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
static void Reposition(HWND hDlg, int id, int dx, int dw)
|
|
|
|
{
|
|
|
|
HWND hCtrl;
|
|
|
|
RECT rc;
|
|
|
|
|
|
|
|
hCtrl = GetDlgItem(hDlg, id);
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
|
|
|
SetWindowPos(hCtrl, HWND_TOP, rc.left + dx, rc.top, rc.right - rc.left + dw, rc.bottom - rc.top, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PositionControls(HWND hDlg)
|
|
|
|
{
|
|
|
|
RECT rc;
|
2018-05-11 16:35:48 +00:00
|
|
|
HWND hCtrl, hPrevCtrl;
|
2018-03-22 23:14:20 +00:00
|
|
|
int i, ow, dw; // original width, delta
|
|
|
|
|
|
|
|
// Get the original size of the control
|
|
|
|
GetWindowRect(GetDlgItem(hDlg, IDS_UPDATE_FREQUENCY_TXT), &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
|
|
|
ow = rc.right - rc.left;
|
|
|
|
dw = GetTextWidth(hDlg, IDS_UPDATE_FREQUENCY_TXT) - ow;
|
|
|
|
dw = max(dw, GetTextWidth(hDlg, IDS_INCLUDE_BETAS_TXT) - ow);
|
|
|
|
if (dw > 0) {
|
|
|
|
GetWindowRect(hDlg, &rc);
|
|
|
|
SetWindowPos(hDlg, NULL, -1, -1, rc.right - rc.left + dw, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
for (i = 0; i < ARRAYSIZE(update_settings_reposition_ids); i++)
|
|
|
|
Reposition(hDlg, update_settings_reposition_ids[i], (i < 4) ? 0 : dw, (i >= 4) ? 0 : dw);
|
|
|
|
}
|
|
|
|
|
|
|
|
hCtrl = GetDlgItem(hDlg, IDC_UPDATE_FREQUENCY);
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
|
|
|
ow = rc.right - rc.left;
|
|
|
|
|
|
|
|
dw = GetTextSize(hCtrl, lmprintf(MSG_013)).cx;
|
|
|
|
dw = max(dw, GetTextSize(hCtrl, lmprintf(MSG_030, lmprintf(MSG_014))).cx);
|
|
|
|
dw = max(dw, GetTextSize(hCtrl, lmprintf(MSG_015)).cx);
|
|
|
|
dw = max(dw, GetTextSize(hCtrl, lmprintf(MSG_016)).cx);
|
|
|
|
dw = max(dw, GetTextSize(hCtrl, lmprintf(MSG_008)).cx);
|
|
|
|
dw = max(dw, GetTextSize(hCtrl, lmprintf(MSG_009)).cx);
|
|
|
|
dw -= ow - ddw;
|
|
|
|
if (dw > 0) {
|
|
|
|
GetWindowRect(hDlg, &rc);
|
|
|
|
SetWindowPos(hDlg, NULL, -1, -1, rc.right - rc.left + dw, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
for (i = 0; i < ARRAYSIZE(update_settings_reposition_ids); i++) {
|
|
|
|
if ((i >= 2) && (i <= 3))
|
|
|
|
continue;
|
|
|
|
Reposition(hDlg, update_settings_reposition_ids[i], (i < 6) ? 0 : dw, (i >= 6) ? 0 : dw);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GetWindowRect(GetDlgItem(hDlg, IDC_CHECK_NOW), &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
|
|
|
ow = rc.right - rc.left;
|
|
|
|
dw = GetTextWidth(hDlg, IDC_CHECK_NOW) - ow + cbw;
|
|
|
|
dw = max(dw, GetTextWidth(hDlg, IDCANCEL) - ow + cbw);
|
|
|
|
if (dw > 0) {
|
|
|
|
GetWindowRect(hDlg, &rc);
|
|
|
|
SetWindowPos(hDlg, NULL, -1, -1, rc.right - rc.left + dw, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
for (i = 0; i < ARRAYSIZE(update_settings_reposition_ids); i++) {
|
|
|
|
if ((i >= 1) && (i <= 5))
|
|
|
|
continue;
|
|
|
|
Reposition(hDlg, update_settings_reposition_ids[i], 0, dw);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hCtrl = GetDlgItem(hDlg, IDC_CHECK_NOW);
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
2018-05-11 16:35:48 +00:00
|
|
|
hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
|
|
|
|
SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top, rc.right - rc.left, ddbh, 0);
|
2018-03-22 23:14:20 +00:00
|
|
|
hCtrl = GetDlgItem(hDlg, IDCANCEL);
|
|
|
|
GetWindowRect(hCtrl, &rc);
|
|
|
|
MapWindowPoints(NULL, hDlg, (POINT*)&rc, 2);
|
2018-05-11 16:35:48 +00:00
|
|
|
hPrevCtrl = GetNextWindow(hCtrl, GW_HWNDPREV);
|
|
|
|
SetWindowPos(hCtrl, hPrevCtrl, rc.left, rc.top, rc.right - rc.left, ddbh, 0);
|
2018-03-22 23:14:20 +00:00
|
|
|
}
|
|
|
|
|
2012-11-29 23:14:36 +00:00
|
|
|
/*
|
|
|
|
* Update policy and settings dialog callback
|
|
|
|
*/
|
|
|
|
INT_PTR CALLBACK UpdateCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
2018-03-22 23:14:20 +00:00
|
|
|
int i, dy;
|
2015-09-25 23:30:16 +00:00
|
|
|
RECT rect;
|
|
|
|
REQRESIZE* rsz;
|
2012-12-02 03:50:08 +00:00
|
|
|
HWND hPolicy;
|
|
|
|
static HWND hFrequency, hBeta;
|
2012-12-16 00:29:37 +00:00
|
|
|
int32_t freq;
|
2013-10-15 21:58:27 +00:00
|
|
|
char update_policy_text[4096];
|
2015-10-18 20:56:24 +00:00
|
|
|
static BOOL resized_already = TRUE;
|
2012-11-29 23:14:36 +00:00
|
|
|
|
|
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG:
|
2015-10-18 20:56:24 +00:00
|
|
|
resized_already = FALSE;
|
2015-05-08 22:37:22 +00:00
|
|
|
hUpdatesDlg = hDlg;
|
2013-10-15 21:58:27 +00:00
|
|
|
apply_localization(IDD_UPDATE_POLICY, hDlg);
|
2018-03-22 23:14:20 +00:00
|
|
|
PositionControls(hDlg);
|
2012-12-05 01:53:10 +00:00
|
|
|
SetTitleBarIcon(hDlg);
|
2019-03-07 16:29:43 +00:00
|
|
|
CenterDialog(hDlg, NULL);
|
2012-12-02 03:50:08 +00:00
|
|
|
hFrequency = GetDlgItem(hDlg, IDC_UPDATE_FREQUENCY);
|
2012-12-16 00:29:37 +00:00
|
|
|
hBeta = GetDlgItem(hDlg, IDC_INCLUDE_BETAS);
|
2013-10-15 21:58:27 +00:00
|
|
|
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));
|
2015-01-25 00:56:38 +00:00
|
|
|
freq = ReadSetting32(SETTING_UPDATE_INTERVAL);
|
2012-12-09 21:17:17 +00:00
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_CHECK_NOW), (freq != 0));
|
2019-02-05 17:49:27 +00:00
|
|
|
EnableWindow(hBeta, (freq >= 0) && is_x86_32);
|
2012-12-07 00:54:40 +00:00
|
|
|
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:
|
2013-10-15 21:58:27 +00:00
|
|
|
IGNORE_RETVAL(ComboBox_SetItemData(hFrequency, ComboBox_AddStringU(hFrequency, lmprintf(MSG_017)), freq));
|
2012-12-07 00:54:40 +00:00
|
|
|
IGNORE_RETVAL(ComboBox_SetCurSel(hFrequency, 4));
|
|
|
|
break;
|
|
|
|
}
|
2013-10-15 21:58:27 +00:00
|
|
|
IGNORE_RETVAL(ComboBox_AddStringU(hBeta, lmprintf(MSG_008)));
|
|
|
|
IGNORE_RETVAL(ComboBox_AddStringU(hBeta, lmprintf(MSG_009)));
|
2019-02-05 17:49:27 +00:00
|
|
|
IGNORE_RETVAL(ComboBox_SetCurSel(hBeta, (ReadSettingBool(SETTING_INCLUDE_BETAS) && is_x86_32) ? 0 : 1));
|
2012-12-02 03:50:08 +00:00
|
|
|
hPolicy = GetDlgItem(hDlg, IDC_POLICY);
|
2012-11-29 23:14:36 +00:00
|
|
|
SendMessage(hPolicy, EM_AUTOURLDETECT, 1, 0);
|
2017-08-10 18:43:04 +00:00
|
|
|
static_sprintf(update_policy_text, update_policy, lmprintf(MSG_179|MSG_RTF),
|
2015-10-21 23:40:24 +00:00
|
|
|
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));
|
2013-10-15 21:58:27 +00:00
|
|
|
SendMessageA(hPolicy, EM_SETTEXTEX, (WPARAM)&friggin_microsoft_unicode_amateurs, (LPARAM)update_policy_text);
|
2012-11-29 23:14:36 +00:00
|
|
|
SendMessage(hPolicy, EM_SETSEL, -1, -1);
|
2015-09-25 23:30:16 +00:00
|
|
|
SendMessage(hPolicy, EM_SETEVENTMASK, 0, ENM_LINK|ENM_REQUESTRESIZE);
|
2012-11-29 23:14:36 +00:00
|
|
|
SendMessageA(hPolicy, EM_SETBKGNDCOLOR, 0, (LPARAM)GetSysColor(COLOR_BTNFACE));
|
2015-09-25 23:30:16 +00:00
|
|
|
SendMessage(hPolicy, EM_REQUESTRESIZE, 0, 0);
|
|
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
2015-10-18 20:56:24 +00:00
|
|
|
if ((((LPNMHDR)lParam)->code == EN_REQUESTRESIZE) && (!resized_already)) {
|
|
|
|
resized_already = TRUE;
|
2015-09-25 23:30:16 +00:00
|
|
|
hPolicy = GetDlgItem(hDlg, IDC_POLICY);
|
|
|
|
GetWindowRect(hPolicy, &rect);
|
|
|
|
dy = rect.bottom - rect.top;
|
|
|
|
rsz = (REQRESIZE *)lParam;
|
2015-10-18 20:31:47 +00:00
|
|
|
dy -= rsz->rc.bottom - rsz->rc.top + 6; // add the border
|
2015-09-25 23:30:16 +00:00
|
|
|
ResizeMoveCtrl(hDlg, hDlg, 0, 0, 0, -dy, 1.0f);
|
|
|
|
ResizeMoveCtrl(hDlg, hPolicy, 0, 0, 0, -dy, 1.0f);
|
2018-03-22 23:14:20 +00:00
|
|
|
for (i = 1; i < ARRAYSIZE(update_settings_reposition_ids); i++)
|
|
|
|
ResizeMoveCtrl(hDlg, GetDlgItem(hDlg, update_settings_reposition_ids[i]), 0, -dy, 0, 0, 1.0f);
|
2015-09-25 23:30:16 +00:00
|
|
|
}
|
2012-11-29 23:14:36 +00:00
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
switch (LOWORD(wParam)) {
|
2012-12-02 03:50:08 +00:00
|
|
|
case IDCLOSE:
|
2012-11-29 23:14:36 +00:00
|
|
|
case IDCANCEL:
|
2013-10-15 21:58:27 +00:00
|
|
|
reset_localization(IDD_UPDATE_POLICY);
|
2012-11-29 23:14:36 +00:00
|
|
|
EndDialog(hDlg, LOWORD(wParam));
|
2015-05-08 22:37:22 +00:00
|
|
|
hUpdatesDlg = NULL;
|
2012-11-29 23:14:36 +00:00
|
|
|
return (INT_PTR)TRUE;
|
2012-12-09 20:36:29 +00:00
|
|
|
case IDC_CHECK_NOW:
|
|
|
|
CheckForUpdates(TRUE);
|
|
|
|
return (INT_PTR)TRUE;
|
2012-12-02 03:50:08 +00:00
|
|
|
case IDC_UPDATE_FREQUENCY:
|
|
|
|
if (HIWORD(wParam) != CBN_SELCHANGE)
|
|
|
|
break;
|
2012-12-16 22:42:30 +00:00
|
|
|
freq = (int32_t)ComboBox_GetItemData(hFrequency, ComboBox_GetCurSel(hFrequency));
|
2015-01-25 00:56:38 +00:00
|
|
|
WriteSetting32(SETTING_UPDATE_INTERVAL, (DWORD)freq);
|
2012-12-16 00:29:37 +00:00
|
|
|
EnableWindow(hBeta, (freq >= 0));
|
2012-12-02 03:50:08 +00:00
|
|
|
return (INT_PTR)TRUE;
|
|
|
|
case IDC_INCLUDE_BETAS:
|
|
|
|
if (HIWORD(wParam) != CBN_SELCHANGE)
|
|
|
|
break;
|
2015-01-25 00:56:38 +00:00
|
|
|
WriteSettingBool(SETTING_INCLUDE_BETAS, ComboBox_GetCurSel(hBeta) == 0);
|
2012-12-02 03:50:08 +00:00
|
|
|
return (INT_PTR)TRUE;
|
2012-11-29 23:14:36 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (INT_PTR)FALSE;
|
|
|
|
}
|
2012-12-02 03:50:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Initial update check setup
|
|
|
|
*/
|
|
|
|
BOOL SetUpdateCheck(void)
|
|
|
|
{
|
|
|
|
BOOL enable_updates;
|
2017-11-13 14:29:48 +00:00
|
|
|
uint64_t commcheck = GetTickCount64();
|
2012-12-16 22:42:30 +00:00
|
|
|
char filename[MAX_PATH] = "", exename[] = APPLICATION_NAME ".exe";
|
|
|
|
size_t fn_len, exe_len;
|
2012-12-02 03:50:08 +00:00
|
|
|
|
2015-01-25 00:56:38 +00:00
|
|
|
// Test if we can read and write settings. If not, forget it.
|
2016-02-24 16:10:54 +00:00
|
|
|
WriteSetting64(SETTING_COMM_CHECK, commcheck);
|
|
|
|
if (ReadSetting64(SETTING_COMM_CHECK) != commcheck)
|
2012-12-02 03:50:08 +00:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
// If the update interval is not set, this is the first time we run so prompt the user
|
2015-01-25 00:56:38 +00:00
|
|
|
if (ReadSetting32(SETTING_UPDATE_INTERVAL) == 0) {
|
2018-11-20 12:28:20 +00:00
|
|
|
notification_info more_info;
|
2012-12-16 22:42:30 +00:00
|
|
|
|
|
|
|
// 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
|
2012-12-16 22:42:30 +00:00
|
|
|
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
|
2018-11-20 12:28:20 +00:00
|
|
|
more_info.id = IDD_UPDATE_POLICY;
|
|
|
|
more_info.callback = UpdateCallback;
|
|
|
|
enable_updates = Notification(MSG_QUESTION, NULL, &more_info, lmprintf(MSG_004), lmprintf(MSG_005));
|
2014-11-27 23:24:50 +00:00
|
|
|
#if !defined(_DEBUG)
|
2012-12-16 22:42:30 +00:00
|
|
|
}
|
2014-11-27 23:24:50 +00:00
|
|
|
#endif
|
2012-12-02 03:50:08 +00:00
|
|
|
if (!enable_updates) {
|
2015-01-25 00:56:38 +00:00
|
|
|
WriteSetting32(SETTING_UPDATE_INTERVAL, -1);
|
2012-12-02 03:50:08 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
// If the user hasn't set the interval in the dialog, set to default
|
2015-01-25 00:56:38 +00:00
|
|
|
if ( (ReadSetting32(SETTING_UPDATE_INTERVAL) == 0) ||
|
|
|
|
((ReadSetting32(SETTING_UPDATE_INTERVAL) == -1) && enable_updates) )
|
|
|
|
WriteSetting32(SETTING_UPDATE_INTERVAL, 86400);
|
2012-12-02 03:50:08 +00:00
|
|
|
}
|
2019-03-04 19:14:54 +00:00
|
|
|
// Also detect if we can use Fido, which depends on:
|
2019-03-12 14:20:19 +00:00
|
|
|
// - Powershell being installed
|
[net] add Windows retail ISO downloads
* This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED*
PowerShell script, that is downloaded from GitHub and that resides in memory for
the duration of a session.
* The reason we use a downloaded PS script, rather than an embedded on, is because:
- Microsoft have regularly been changing the deal with regards to how retail ISOs
can be downloaded, and not for the better, so we can't simply embed a static
means of downloading ISOs and expect that to work forever.
- By using an external script, we can immediately respond to whatever new means of
*ANNOYING* their legitimate users Microsoft will come up with next, as well as
make sure that, the minute a new retail version of Windows becomes available, it
also becomes available for download in Rufus.
* Note that if you are concerned about downloading a remote PS script that is being
run at the same level as an elevated application, you should understand that:
- Only scripts downloaded from GitHub, from an account that is protected with 2FA,
are allowed to run (i.e. someone would first have to steal a *physical* 2FA key
to be in a position to upload a malicious script).
- On top of this, only scripts that are signed with a separate private key (RSA +
AES-256), that is itself also protected with a strong unique password which only
a single person knows (and must manually enter each time they want to make a new
version of the script available for download), are allowed to run.
The above means that there's about as much chance for someone to manage to upload
a malicious script on the GitHub servers, that Rufus would allow to run, as there
is for someone to upload a malicious version of Rufus itself.
Still, if you are paranoid and have concerns that, even as you can validate from
its source that Rufus does not attempt to execute any remote script unless a user
actively selected and clicked the DOWNLOAD button, you can also completely disable
the remote script download feature, if you just set the update check to disabled
(which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to
enable or not, the very first time you run the application).
* Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
|
|
|
// - Update check being enabled
|
|
|
|
// - URL for the script being reachable
|
2019-03-12 14:20:19 +00:00
|
|
|
if (((ReadRegistryKey32(REGKEY_HKLM, "Microsoft\\PowerShell\\1\\Install") > 0) ||
|
|
|
|
(ReadRegistryKey32(REGKEY_HKLM, "Microsoft\\PowerShell\\3\\Install") > 0)) &&
|
|
|
|
(ReadSetting32(SETTING_UPDATE_INTERVAL) > 0)) {
|
|
|
|
char *loc = NULL;
|
|
|
|
// Get the Fido URL from parsing a 'Fido.ver' on our server. This enables the use of different
|
|
|
|
// Fido versions from different versions of Rufus, if needed, as opposed to always downloading
|
|
|
|
// the latest release from GitHub, which may contain incompatible changes...
|
|
|
|
uint64_t loc_len = DownloadToFileOrBuffer(RUFUS_URL "/Fido.ver", NULL, (BYTE**)&loc, NULL, FALSE);
|
|
|
|
if ((loc_len != 0) && (loc_len < 4 * KB)) {
|
|
|
|
loc_len++; // DownloadToFileOrBuffer allocated an extra NUL character if needed
|
|
|
|
fido_url = get_token_data_buffer(FIDO_VERSION, 1, loc, (size_t)loc_len);
|
|
|
|
uprintf("Fido URL is %s", fido_url);
|
|
|
|
enable_fido = IsDownloadable(fido_url);
|
2019-03-04 19:14:54 +00:00
|
|
|
}
|
2019-03-12 14:20:19 +00:00
|
|
|
safe_free(loc);
|
[net] add Windows retail ISO downloads
* This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED*
PowerShell script, that is downloaded from GitHub and that resides in memory for
the duration of a session.
* The reason we use a downloaded PS script, rather than an embedded on, is because:
- Microsoft have regularly been changing the deal with regards to how retail ISOs
can be downloaded, and not for the better, so we can't simply embed a static
means of downloading ISOs and expect that to work forever.
- By using an external script, we can immediately respond to whatever new means of
*ANNOYING* their legitimate users Microsoft will come up with next, as well as
make sure that, the minute a new retail version of Windows becomes available, it
also becomes available for download in Rufus.
* Note that if you are concerned about downloading a remote PS script that is being
run at the same level as an elevated application, you should understand that:
- Only scripts downloaded from GitHub, from an account that is protected with 2FA,
are allowed to run (i.e. someone would first have to steal a *physical* 2FA key
to be in a position to upload a malicious script).
- On top of this, only scripts that are signed with a separate private key (RSA +
AES-256), that is itself also protected with a strong unique password which only
a single person knows (and must manually enter each time they want to make a new
version of the script available for download), are allowed to run.
The above means that there's about as much chance for someone to manage to upload
a malicious script on the GitHub servers, that Rufus would allow to run, as there
is for someone to upload a malicious version of Rufus itself.
Still, if you are paranoid and have concerns that, even as you can validate from
its source that Rufus does not attempt to execute any remote script unless a user
actively selected and clicked the DOWNLOAD button, you can also completely disable
the remote script download feature, if you just set the update check to disabled
(which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to
enable or not, the very first time you run the application).
* Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
|
|
|
}
|
2019-03-19 12:04:29 +00:00
|
|
|
if (!enable_fido) {
|
|
|
|
ubprintf("Notice: The ISO download feature has been deactivated because %s", (ReadSetting32(SETTING_UPDATE_INTERVAL) <= 0) ?
|
|
|
|
"'Check for updates' is disabled in your settings." : "the remote download script can not be accessed.");
|
|
|
|
}
|
2012-12-02 03:50:08 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
2012-12-04 01:47:45 +00:00
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
void CreateStaticFont(HDC hDC, HFONT* hFont, BOOL underlined)
|
|
|
|
{
|
2013-10-15 21:58:27 +00:00
|
|
|
TEXTMETRIC tm;
|
|
|
|
LOGFONT lf;
|
|
|
|
|
2018-03-22 23:14:20 +00:00
|
|
|
if (*hFont != NULL)
|
2013-10-15 21:58:27 +00:00
|
|
|
return;
|
2018-03-22 23:14:20 +00:00
|
|
|
GetTextMetrics(hDC, &tm);
|
2013-10-15 21:58:27 +00:00
|
|
|
lf.lfHeight = tm.tmHeight;
|
|
|
|
lf.lfWidth = 0;
|
|
|
|
lf.lfEscapement = 0;
|
|
|
|
lf.lfOrientation = 0;
|
|
|
|
lf.lfWeight = tm.tmWeight;
|
|
|
|
lf.lfItalic = tm.tmItalic;
|
2018-03-22 23:14:20 +00:00
|
|
|
lf.lfUnderline = underlined;
|
2013-10-15 21:58:27 +00:00
|
|
|
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;
|
2018-03-22 23:14:20 +00:00
|
|
|
GetTextFace(hDC, LF_FACESIZE, lf.lfFaceName);
|
|
|
|
*hFont = CreateFontIndirect(&lf);
|
2013-10-15 21:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2015-05-14 23:36:42 +00:00
|
|
|
INT_PTR CALLBACK update_subclass_callback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
2013-10-15 21:58:27 +00:00
|
|
|
{
|
|
|
|
switch (message)
|
|
|
|
{
|
|
|
|
case WM_SETCURSOR:
|
|
|
|
if ((HWND)wParam == GetDlgItem(hDlg, IDC_WEBSITE)) {
|
|
|
|
SetCursor(LoadCursor(NULL, IDC_HAND));
|
|
|
|
return (INT_PTR)TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2015-05-14 23:36:42 +00:00
|
|
|
return CallWindowProc(update_original_proc, hDlg, message, wParam, lParam);
|
2013-10-15 21:58:27 +00:00
|
|
|
}
|
|
|
|
|
2012-12-04 01:47:45 +00:00
|
|
|
/*
|
|
|
|
* New version notification dialog
|
|
|
|
*/
|
|
|
|
INT_PTR CALLBACK NewVersionCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
2013-12-02 18:24:54 +00:00
|
|
|
char cmdline[] = APPLICATION_NAME " -w 150";
|
2012-12-06 01:40:44 +00:00
|
|
|
static char* filepath = NULL;
|
|
|
|
static int download_status = 0;
|
2018-03-22 23:14:20 +00:00
|
|
|
static HFONT hyperlink_font = NULL;
|
2018-06-30 21:45:15 +00:00
|
|
|
static HANDLE hThread = NULL;
|
2015-10-13 22:29:30 +00:00
|
|
|
HWND hNotes;
|
2018-06-30 21:45:15 +00:00
|
|
|
DWORD exit_code;
|
2012-12-06 01:40:44 +00:00
|
|
|
STARTUPINFOA si;
|
|
|
|
PROCESS_INFORMATION pi;
|
2014-05-22 21:46:36 +00:00
|
|
|
EXT_DECL(dl_ext, NULL, __VA_GROUP__("*.exe"), __VA_GROUP__(lmprintf(MSG_037)));
|
2012-12-04 01:47:45 +00:00
|
|
|
|
|
|
|
switch (message) {
|
|
|
|
case WM_INITDIALOG:
|
2013-10-15 21:58:27 +00:00
|
|
|
apply_localization(IDD_NEW_VERSION, hDlg);
|
2012-12-06 01:40:44 +00:00
|
|
|
download_status = 0;
|
2012-12-05 01:53:10 +00:00
|
|
|
SetTitleBarIcon(hDlg);
|
2019-03-07 16:29:43 +00:00
|
|
|
CenterDialog(hDlg, NULL);
|
2013-10-15 21:58:27 +00:00
|
|
|
// Subclass the callback so that we can change the cursor
|
2015-05-14 23:36:42 +00:00
|
|
|
update_original_proc = (WNDPROC)SetWindowLongPtr(hDlg, GWLP_WNDPROC, (LONG_PTR)update_subclass_callback);
|
2012-12-04 01:47:45 +00:00
|
|
|
hNotes = GetDlgItem(hDlg, IDC_RELEASE_NOTES);
|
|
|
|
SendMessage(hNotes, EM_AUTOURLDETECT, 1, 0);
|
2012-12-05 01:53:10 +00:00
|
|
|
SendMessageA(hNotes, EM_SETTEXTEX, (WPARAM)&friggin_microsoft_unicode_amateurs, (LPARAM)update.release_notes);
|
2012-12-04 01:47:45 +00:00
|
|
|
SendMessage(hNotes, EM_SETSEL, -1, -1);
|
|
|
|
SendMessage(hNotes, EM_SETEVENTMASK, 0, ENM_LINK);
|
2015-10-18 20:31:47 +00:00
|
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_YOUR_VERSION), lmprintf(MSG_018,
|
2015-02-08 22:36:57 +00:00
|
|
|
rufus_version[0], rufus_version[1], rufus_version[2]));
|
2013-10-15 21:58:27 +00:00
|
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_LATEST_VERSION), lmprintf(MSG_019,
|
2015-02-08 22:36:57 +00:00
|
|
|
update.version[0], update.version[1], update.version[2]));
|
2013-10-15 21:58:27 +00:00
|
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_DOWNLOAD_URL), update.download_url);
|
2013-01-09 21:54:28 +00:00
|
|
|
SendMessage(GetDlgItem(hDlg, IDC_PROGRESS), PBM_SETRANGE, 0, (MAX_PROGRESS<<16) & 0xFFFF0000);
|
2012-12-06 01:40:44 +00:00
|
|
|
if (update.download_url == NULL)
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_DOWNLOAD), FALSE);
|
2018-03-22 23:14:20 +00:00
|
|
|
ResizeButtonHeight(hDlg, IDCANCEL);
|
2012-12-04 01:47:45 +00:00
|
|
|
break;
|
2013-10-15 21:58:27 +00:00
|
|
|
case WM_CTLCOLORSTATIC:
|
|
|
|
if ((HWND)lParam != GetDlgItem(hDlg, IDC_WEBSITE))
|
|
|
|
return FALSE;
|
|
|
|
// Change the font for the hyperlink
|
|
|
|
SetBkMode((HDC)wParam, TRANSPARENT);
|
2018-03-22 23:14:20 +00:00
|
|
|
CreateStaticFont((HDC)wParam, &hyperlink_font, TRUE);
|
2013-10-15 21:58:27 +00:00
|
|
|
SelectObject((HDC)wParam, hyperlink_font);
|
|
|
|
SetTextColor((HDC)wParam, RGB(0,0,125)); // DARK_BLUE
|
|
|
|
return (INT_PTR)CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
|
2012-12-04 01:47:45 +00:00
|
|
|
case WM_COMMAND:
|
|
|
|
switch (LOWORD(wParam)) {
|
|
|
|
case IDCLOSE:
|
|
|
|
case IDCANCEL:
|
2014-11-01 01:38:40 +00:00
|
|
|
if (download_status != 1) {
|
|
|
|
reset_localization(IDD_NEW_VERSION);
|
|
|
|
safe_free(filepath);
|
|
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
|
|
}
|
2012-12-04 01:47:45 +00:00
|
|
|
return (INT_PTR)TRUE;
|
2013-10-15 21:58:27 +00:00
|
|
|
case IDC_WEBSITE:
|
|
|
|
ShellExecuteA(hDlg, "open", RUFUS_URL, NULL, NULL, SW_SHOWNORMAL);
|
|
|
|
break;
|
2012-12-16 22:42:30 +00:00
|
|
|
case IDC_DOWNLOAD: // Also doubles as abort and launch function
|
2012-12-06 01:40:44 +00:00
|
|
|
switch(download_status) {
|
|
|
|
case 1: // Abort
|
|
|
|
FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_CANCELLED;
|
2014-11-01 01:38:40 +00:00
|
|
|
download_status = 0;
|
2018-06-30 21:45:15 +00:00
|
|
|
hThread = NULL;
|
2012-12-06 01:40:44 +00:00
|
|
|
break;
|
|
|
|
case 2: // Launch newer version and close this one
|
2018-06-30 21:45:15 +00:00
|
|
|
if ((hThread == NULL) || (!GetExitCodeThread(hThread, &exit_code)) || (exit_code == 0)) {
|
|
|
|
hThread = NULL;
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_DOWNLOAD), FALSE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
hThread = NULL;
|
2014-11-01 01:38:40 +00:00
|
|
|
Sleep(1000); // Add a delay on account of antivirus scanners
|
2015-10-13 22:29:30 +00:00
|
|
|
|
[pki] fix https://www.kb.cert.org/vuls/id/403768
* 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.
2017-08-31 11:13:51 +00:00
|
|
|
if (ValidateSignature(hDlg, filepath) != NO_ERROR) {
|
|
|
|
// Unconditionally delete the download and disable the "Launch" control
|
[net] add Windows retail ISO downloads
* This is accomplished through Fido (https://github.com/pbatard/Fido), a *SIGNED*
PowerShell script, that is downloaded from GitHub and that resides in memory for
the duration of a session.
* The reason we use a downloaded PS script, rather than an embedded on, is because:
- Microsoft have regularly been changing the deal with regards to how retail ISOs
can be downloaded, and not for the better, so we can't simply embed a static
means of downloading ISOs and expect that to work forever.
- By using an external script, we can immediately respond to whatever new means of
*ANNOYING* their legitimate users Microsoft will come up with next, as well as
make sure that, the minute a new retail version of Windows becomes available, it
also becomes available for download in Rufus.
* Note that if you are concerned about downloading a remote PS script that is being
run at the same level as an elevated application, you should understand that:
- Only scripts downloaded from GitHub, from an account that is protected with 2FA,
are allowed to run (i.e. someone would first have to steal a *physical* 2FA key
to be in a position to upload a malicious script).
- On top of this, only scripts that are signed with a separate private key (RSA +
AES-256), that is itself also protected with a strong unique password which only
a single person knows (and must manually enter each time they want to make a new
version of the script available for download), are allowed to run.
The above means that there's about as much chance for someone to manage to upload
a malicious script on the GitHub servers, that Rufus would allow to run, as there
is for someone to upload a malicious version of Rufus itself.
Still, if you are paranoid and have concerns that, even as you can validate from
its source that Rufus does not attempt to execute any remote script unless a user
actively selected and clicked the DOWNLOAD button, you can also completely disable
the remote script download feature, if you just set the update check to disabled
(which, by the way, Rufus *EXPLICITLY* asks you to choose whether you want to
enable or not, the very first time you run the application).
* Also remove _unlinkU() which duplicates what DeleteFileU() already does.
2019-03-02 23:28:56 +00:00
|
|
|
DeleteFileU(filepath);
|
[pki] fix https://www.kb.cert.org/vuls/id/403768
* 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.
2017-08-31 11:13:51 +00:00
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_DOWNLOAD), FALSE);
|
2015-10-13 22:29:30 +00:00
|
|
|
break;
|
[pki] fix https://www.kb.cert.org/vuls/id/403768
* 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.
2017-08-31 11:13:51 +00:00
|
|
|
}
|
2015-10-13 22:29:30 +00:00
|
|
|
|
2012-12-06 01:40:44 +00:00
|
|
|
memset(&si, 0, sizeof(si));
|
|
|
|
memset(&pi, 0, sizeof(pi));
|
|
|
|
si.cb = sizeof(si);
|
2013-11-30 17:39:38 +00:00
|
|
|
if (!CreateProcessU(filepath, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
|
2015-01-01 23:39:28 +00:00
|
|
|
PrintInfo(0, MSG_214);
|
2018-06-29 17:19:05 +00:00
|
|
|
uprintf("Failed to launch new application: %s", WindowsErrorString());
|
2012-12-06 01:40:44 +00:00
|
|
|
} else {
|
2015-01-01 23:39:28 +00:00
|
|
|
PrintInfo(0, MSG_213);
|
2012-12-06 01:40:44 +00:00
|
|
|
PostMessage(hDlg, WM_COMMAND, (WPARAM)IDCLOSE, 0);
|
|
|
|
PostMessage(hMainDialog, WM_CLOSE, 0, 0);
|
2012-12-05 01:53:10 +00:00
|
|
|
}
|
2012-12-06 01:40:44 +00:00
|
|
|
break;
|
|
|
|
default: // Download
|
2013-12-02 18:24:54 +00:00
|
|
|
if (update.download_url == NULL) {
|
2018-06-29 17:19:05 +00:00
|
|
|
uprintf("Could not get download URL");
|
2013-12-02 18:24:54 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-06-30 21:45:15 +00:00
|
|
|
dl_ext.filename = PathFindFileNameU(update.download_url);
|
2014-05-22 00:52:25 +00:00
|
|
|
filepath = FileDialog(TRUE, app_dir, &dl_ext, OFN_NOCHANGEDIR);
|
2013-12-02 18:24:54 +00:00
|
|
|
if (filepath == NULL) {
|
2018-06-29 17:19:05 +00:00
|
|
|
uprintf("Could not get save path");
|
2013-12-02 18:24:54 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-05-08 22:37:22 +00:00
|
|
|
// Opening the File Dialog will make us lose tabbing focus - set it back
|
|
|
|
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDC_DOWNLOAD), TRUE);
|
2018-06-30 21:45:15 +00:00
|
|
|
hThread = DownloadSignedFileThreaded(update.download_url, filepath, hDlg, TRUE);
|
2012-12-06 01:40:44 +00:00
|
|
|
break;
|
2012-12-04 01:47:45 +00:00
|
|
|
}
|
|
|
|
return (INT_PTR)TRUE;
|
|
|
|
}
|
|
|
|
break;
|
2014-05-19 22:25:00 +00:00
|
|
|
case UM_PROGRESS_INIT:
|
2014-11-01 01:38:40 +00:00
|
|
|
EnableWindow(GetDlgItem(hDlg, IDCANCEL), FALSE);
|
|
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_DOWNLOAD), lmprintf(MSG_038));
|
2012-12-06 01:40:44 +00:00
|
|
|
FormatStatus = 0;
|
|
|
|
download_status = 1;
|
|
|
|
return (INT_PTR)TRUE;
|
2014-05-19 22:25:00 +00:00
|
|
|
case UM_PROGRESS_EXIT:
|
2014-11-01 01:38:40 +00:00
|
|
|
EnableWindow(GetDlgItem(hDlg, IDCANCEL), TRUE);
|
2018-06-30 21:45:15 +00:00
|
|
|
if (wParam != 0) {
|
2013-10-15 21:58:27 +00:00
|
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_DOWNLOAD), lmprintf(MSG_039));
|
2012-12-06 01:40:44 +00:00
|
|
|
download_status = 2;
|
|
|
|
} else {
|
2013-10-15 21:58:27 +00:00
|
|
|
SetWindowTextU(GetDlgItem(hDlg, IDC_DOWNLOAD), lmprintf(MSG_040));
|
2018-06-30 21:45:15 +00:00
|
|
|
// Disable the download button if we found an invalid signature
|
|
|
|
EnableWindow(GetDlgItem(hDlg, IDC_DOWNLOAD),
|
|
|
|
FormatStatus != (ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_BAD_SIGNATURE)));
|
2012-12-06 01:40:44 +00:00
|
|
|
download_status = 0;
|
|
|
|
}
|
|
|
|
return (INT_PTR)TRUE;
|
2012-12-04 01:47:45 +00:00
|
|
|
}
|
|
|
|
return (INT_PTR)FALSE;
|
|
|
|
}
|
|
|
|
|
2012-12-05 01:53:10 +00:00
|
|
|
void DownloadNewVersion(void)
|
2012-12-04 01:47:45 +00:00
|
|
|
{
|
2015-07-03 22:42:45 +00:00
|
|
|
MyDialogBox(hMainInstance, IDD_NEW_VERSION, hMainDialog, NewVersionCallback);
|
2012-12-05 01:53:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2012-12-04 01:47:45 +00:00
|
|
|
}
|
2014-11-28 22:42:22 +00:00
|
|
|
|
|
|
|
// Return the onscreen size of the text displayed by a control
|
2018-03-22 23:14:20 +00:00
|
|
|
SIZE GetTextSize(HWND hCtrl, char* txt)
|
2014-11-28 22:42:22 +00:00
|
|
|
{
|
|
|
|
SIZE sz = {0, 0};
|
|
|
|
HDC hDC;
|
2014-12-15 18:42:21 +00:00
|
|
|
wchar_t *wstr = NULL;
|
2014-11-28 22:42:22 +00:00
|
|
|
int len;
|
|
|
|
HFONT hFont;
|
|
|
|
|
2015-05-14 23:36:42 +00:00
|
|
|
// Compute the size of the text
|
2014-11-28 22:42:22 +00:00
|
|
|
hDC = GetDC(hCtrl);
|
|
|
|
if (hDC == NULL)
|
|
|
|
goto out;
|
|
|
|
hFont = (HFONT)SendMessageA(hCtrl, WM_GETFONT, 0, 0);
|
|
|
|
if (hFont == NULL)
|
|
|
|
goto out;
|
|
|
|
SelectObject(hDC, hFont);
|
2018-03-22 23:14:20 +00:00
|
|
|
if (txt == NULL) {
|
|
|
|
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);
|
|
|
|
} else {
|
|
|
|
wstr = utf8_to_wchar(txt);
|
|
|
|
if (wstr != NULL)
|
|
|
|
GetTextExtentPoint32W(hDC, wstr, (int)wcslen(wstr), &sz);
|
|
|
|
}
|
2014-11-28 22:42:22 +00:00
|
|
|
out:
|
|
|
|
safe_free(wstr);
|
2018-03-22 23:14:20 +00:00
|
|
|
safe_release_dc(hCtrl, hDC);
|
2014-11-28 22:42:22 +00:00
|
|
|
return sz;
|
|
|
|
}
|
2015-07-03 22:42:45 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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?
|
|
|
|
*/
|
|
|
|
|
|
|
|
// 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";
|
2015-07-03 22:42:45 +00:00
|
|
|
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);
|
2015-08-27 17:37:04 +00:00
|
|
|
|
|
|
|
if ((size == 0) || (rcTemplate == NULL)) {
|
|
|
|
safe_free(rcTemplate);
|
2015-07-03 22:42:45 +00:00
|
|
|
return NULL;
|
2015-08-27 17:37:04 +00:00
|
|
|
}
|
2015-07-03 22:42:45 +00:00
|
|
|
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;
|
2018-05-17 19:45:32 +00:00
|
|
|
dwBuf[2] = WS_EX_APPWINDOW | WS_EX_LAYOUTRTL;
|
2015-07-03 22:42:45 +00:00
|
|
|
}
|
2015-12-30 14:27:52 +00:00
|
|
|
|
2016-01-11 13:06:33 +00:00
|
|
|
// All our dialogs are set to use 'Segoe UI Symbol' by default:
|
2017-11-13 14:29:48 +00:00
|
|
|
// 1. So that we can replace the font name with 'Segoe UI'
|
2016-01-11 13:06:33 +00:00
|
|
|
// 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'
|
2015-12-30 14:27:52 +00:00
|
|
|
|
2016-01-11 13:06:33 +00:00
|
|
|
// If 'Segoe UI Symbol' is available, and we are using Thai, we're done here
|
2016-01-12 17:53:08 +00:00
|
|
|
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))
|
2015-12-30 14:27:52 +00:00
|
|
|
return rcTemplate;
|
|
|
|
|
2016-01-11 13:06:33 +00:00
|
|
|
// 'Segoe UI Symbol' cannot be used => Fall back to the best we have
|
2015-07-03 22:42:45 +00:00
|
|
|
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.
|
2017-11-13 14:29:48 +00:00
|
|
|
// 'Segoe UI Symbol' -> 'Segoe UI'
|
|
|
|
wBuf[8] = 0;
|
2015-07-03 22:42:45 +00:00
|
|
|
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)
|
|
|
|
{
|
2017-02-06 12:07:00 +00:00
|
|
|
INT_PTR ret;
|
2015-07-03 22:42:45 +00:00
|
|
|
LPCDLGTEMPLATE rcTemplate = GetDialogTemplate(Dialog_ID);
|
2017-02-06 12:07:00 +00:00
|
|
|
|
|
|
|
// 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);
|
2015-07-03 22:42:45 +00:00
|
|
|
safe_free(rcTemplate);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-07-09 15:20:58 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The following function calls are used to automatically detect and close the native
|
2019-03-05 12:41:10 +00:00
|
|
|
* Windows format prompt "You must format the disk in drive X:" as well as the cookies
|
|
|
|
* alert being popped by Windows when running our Download script. To do that, we use
|
|
|
|
* an event hook that gets triggered whenever a window is placed in the foreground.
|
2016-07-09 15:20:58 +00:00
|
|
|
* In that hook, we look for a dialog that has style WS_POPUPWINDOW and has the relevant
|
2019-03-05 12:41:10 +00:00
|
|
|
* title. However, in case of the Format prompt, 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.
|
2016-07-09 15:20:58 +00:00
|
|
|
* Oh, and since all of these strings are localized, we must first pick them up from
|
2019-03-05 12:41:10 +00:00
|
|
|
* the relevant mui's.
|
2016-07-09 15:20:58 +00:00
|
|
|
*/
|
2019-03-05 12:41:10 +00:00
|
|
|
static BOOL CALLBACK AlertPromptCallback(HWND hWnd, LPARAM lParam)
|
2016-07-09 15:20:58 +00:00
|
|
|
{
|
|
|
|
char str[128];
|
|
|
|
BOOL *found = (BOOL*)lParam;
|
|
|
|
|
|
|
|
if (GetWindowTextU(hWnd, str, sizeof(str)) == 0)
|
|
|
|
return TRUE;
|
2019-03-07 16:29:43 +00:00
|
|
|
if (safe_strcmp(str, button_str) == 0)
|
2016-07-09 15:20:58 +00:00
|
|
|
*found = TRUE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2019-03-05 12:41:10 +00:00
|
|
|
static void CALLBACK AlertPromptHook(HWINEVENTHOOK hWinEventHook, DWORD Event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
|
2016-07-09 15:20:58 +00:00
|
|
|
{
|
|
|
|
char str[128];
|
|
|
|
BOOL found;
|
|
|
|
|
|
|
|
if (Event == EVENT_SYSTEM_FOREGROUND) {
|
2018-08-20 08:46:23 +00:00
|
|
|
if (GetWindowLongPtr(hWnd, GWL_STYLE) & WS_POPUPWINDOW) {
|
2016-07-09 15:20:58 +00:00
|
|
|
str[0] = 0;
|
|
|
|
GetWindowTextU(hWnd, str, sizeof(str));
|
2019-03-07 16:29:43 +00:00
|
|
|
if (strcmp(str, title_str[0]) == 0) {
|
2016-07-09 15:20:58 +00:00
|
|
|
found = FALSE;
|
2019-03-05 12:41:10 +00:00
|
|
|
EnumChildWindows(hWnd, AlertPromptCallback, (LPARAM)&found);
|
2016-07-09 15:20:58 +00:00
|
|
|
if (found) {
|
|
|
|
SendMessage(hWnd, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0);
|
|
|
|
uprintf("Closed Windows format prompt");
|
|
|
|
}
|
2019-03-07 16:29:43 +00:00
|
|
|
} else if (close_fido_cookie_prompts && strcmp(str, title_str[1]) == 0) {
|
2019-03-05 12:41:10 +00:00
|
|
|
SendMessage(hWnd, WM_COMMAND, (WPARAM)IDCANCEL, (LPARAM)0);
|
2019-03-07 16:29:43 +00:00
|
|
|
} else if ((strcmp(str, title_str[2]) == 0) && (hWnd != hFidoDlg)) {
|
|
|
|
// A wild Fido dialog appeared! => Keep track of its handle and center it
|
|
|
|
hFidoDlg = hWnd;
|
|
|
|
CenterDialog(hWnd, hMainDialog);
|
2016-07-09 15:20:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-13 17:01:26 +00:00
|
|
|
void SetAlertPromptMessages(void)
|
2016-07-09 15:20:58 +00:00
|
|
|
{
|
|
|
|
HMODULE mui_lib;
|
|
|
|
char mui_path[MAX_PATH];
|
|
|
|
|
2018-12-14 18:19:33 +00:00
|
|
|
// Fetch the localized strings in the relevant MUI
|
2016-07-09 15:20:58 +00:00
|
|
|
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)
|
2019-03-07 16:29:43 +00:00
|
|
|
if (LoadStringU(mui_lib, 4125, title_str[0], sizeof(title_str[0])) <= 0) {
|
|
|
|
static_strcpy(title_str[0], "Microsoft Windows");
|
2016-07-09 15:20:58 +00:00
|
|
|
uprintf("Warning: Could not locate localized format prompt title string in '%s': %s", mui_path, WindowsErrorString());
|
2019-03-07 16:29:43 +00:00
|
|
|
}
|
|
|
|
if (LoadStringU(mui_lib, 4126, button_str, sizeof(button_str)) <= 0) {
|
|
|
|
static_strcpy(button_str, "Format disk");
|
2016-07-09 15:20:58 +00:00
|
|
|
uprintf("Warning: Could not locate localized format prompt button string in '%s': %s", mui_path, WindowsErrorString());
|
2019-03-07 16:29:43 +00:00
|
|
|
}
|
2016-07-09 15:20:58 +00:00
|
|
|
FreeLibrary(mui_lib);
|
|
|
|
}
|
2019-03-05 12:41:10 +00:00
|
|
|
static_sprintf(mui_path, "%s\\%s\\urlmon.dll.mui", system_dir, GetCurrentMUI());
|
|
|
|
mui_lib = LoadLibraryU(mui_path);
|
|
|
|
if (mui_lib != NULL) {
|
|
|
|
// 2070 = "Windows Security Warning" (yes, that's what MS uses for a stupid cookie!)
|
2019-03-07 16:29:43 +00:00
|
|
|
if (LoadStringU(mui_lib, 2070, title_str[1], sizeof(title_str[1])) <= 0) {
|
|
|
|
static_strcpy(title_str[1], "Windows Security Warning");
|
2019-03-05 12:41:10 +00:00
|
|
|
uprintf("Warning: Could not locate localized cookie prompt title string in '%s': %s", mui_path, WindowsErrorString());
|
2019-03-07 16:29:43 +00:00
|
|
|
}
|
2019-03-05 12:41:10 +00:00
|
|
|
FreeLibrary(mui_lib);
|
|
|
|
}
|
2019-03-12 14:20:19 +00:00
|
|
|
static_strcpy(title_str[2], lmprintf(MSG_149));
|
2019-03-13 17:01:26 +00:00
|
|
|
}
|
2016-07-09 15:20:58 +00:00
|
|
|
|
2019-03-13 17:01:26 +00:00
|
|
|
BOOL SetAlertPromptHook(void)
|
|
|
|
{
|
|
|
|
if (ap_weh != NULL)
|
|
|
|
return TRUE; // No need to set again if active
|
2019-03-05 12:41:10 +00:00
|
|
|
ap_weh = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, NULL,
|
|
|
|
AlertPromptHook, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
|
|
|
return (ap_weh != NULL);
|
2016-07-09 15:20:58 +00:00
|
|
|
}
|
|
|
|
|
2019-03-05 12:41:10 +00:00
|
|
|
void ClrAlertPromptHook(void) {
|
|
|
|
UnhookWinEvent(ap_weh);
|
|
|
|
ap_weh = NULL;
|
2016-07-09 15:20:58 +00:00
|
|
|
}
|
2016-12-13 14:21:51 +00:00
|
|
|
|
2017-07-04 13:51:25 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-05-10 09:51:31 +00:00
|
|
|
// https://docs.microsoft.com/en-us/globalization/localizability/mirroring-in-win32
|
|
|
|
// Note: This function *destroys* the original icon
|
|
|
|
HICON CreateMirroredIcon(HICON hiconOrg)
|
|
|
|
{
|
|
|
|
HDC hdcScreen, hdcBitmap, hdcMask = NULL;
|
|
|
|
HBITMAP hbm, hbmMask, hbmOld, hbmOldMask;
|
|
|
|
BITMAP bm;
|
|
|
|
ICONINFO ii;
|
|
|
|
HICON hicon = NULL;
|
|
|
|
hdcBitmap = CreateCompatibleDC(NULL);
|
|
|
|
if (hdcBitmap) {
|
|
|
|
hdcMask = CreateCompatibleDC(NULL);
|
|
|
|
if (hdcMask) {
|
|
|
|
SetLayout(hdcBitmap, LAYOUT_RTL);
|
|
|
|
SetLayout(hdcMask, LAYOUT_RTL);
|
|
|
|
} else {
|
|
|
|
DeleteDC(hdcBitmap);
|
|
|
|
hdcBitmap = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hdcScreen = GetDC(NULL);
|
|
|
|
if (hdcScreen) {
|
|
|
|
if (hdcBitmap && hdcMask) {
|
|
|
|
if (hiconOrg) {
|
|
|
|
if (GetIconInfo(hiconOrg, &ii) && GetObject(ii.hbmColor, sizeof(BITMAP), &bm)) {
|
|
|
|
// Do the cleanup for the bitmaps.
|
|
|
|
DeleteObject(ii.hbmMask);
|
|
|
|
DeleteObject(ii.hbmColor);
|
|
|
|
ii.hbmMask = ii.hbmColor = NULL;
|
|
|
|
hbm = CreateCompatibleBitmap(hdcScreen, bm.bmWidth, bm.bmHeight);
|
|
|
|
hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
|
|
|
|
hbmOld = (HBITMAP)SelectObject(hdcBitmap, hbm);
|
|
|
|
hbmOldMask = (HBITMAP)SelectObject(hdcMask, hbmMask);
|
|
|
|
DrawIconEx(hdcBitmap, 0, 0, hiconOrg, bm.bmWidth, bm.bmHeight, 0, NULL, DI_IMAGE);
|
|
|
|
DrawIconEx(hdcMask, 0, 0, hiconOrg, bm.bmWidth, bm.bmHeight, 0, NULL, DI_MASK);
|
|
|
|
SelectObject(hdcBitmap, hbmOld);
|
|
|
|
SelectObject(hdcMask, hbmOldMask);
|
|
|
|
|
|
|
|
// Create the new mirrored icon and delete bitmaps
|
|
|
|
ii.hbmMask = hbmMask;
|
|
|
|
ii.hbmColor = hbm;
|
|
|
|
hicon = CreateIconIndirect(&ii);
|
|
|
|
DeleteObject(hbm);
|
|
|
|
DeleteObject(hbmMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ReleaseDC(NULL, hdcScreen);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hdcBitmap)
|
|
|
|
DeleteDC(hdcBitmap);
|
|
|
|
if (hdcMask)
|
|
|
|
DeleteDC(hdcMask);
|
|
|
|
DestroyIcon(hiconOrg);
|
|
|
|
return hicon;
|
|
|
|
}
|
|
|
|
|
2016-12-13 14:21:51 +00:00
|
|
|
#ifdef RUFUS_TEST
|
2017-04-27 22:06:42 +00:00
|
|
|
static __inline LPWORD lpwAlign(LPWORD addr)
|
2016-12-13 14:21:51 +00:00
|
|
|
{
|
|
|
|
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
|