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

947 lines
26 KiB
C
Raw Normal View History

/*
* Rufus: The Reliable USB Formatting Utility
* Standard Dialog Routines (Browse for folder, About, etc)
* Copyright (c) 2011-2012 Pete Batard <pete@akeo.ie>
*
* Based on zadig_stdlg.c, part of libwdi: http://libwdi.sf.net
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
#ifdef _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <shlobj.h>
#include <commdlg.h>
#include "rufus.h"
#include "msapi_utf8.h"
#include "resource.h"
#include "license.h"
/* Windows versions */
enum WindowsVersion {
WINDOWS_UNDEFINED,
WINDOWS_UNSUPPORTED,
WINDOWS_2K,
WINDOWS_XP,
WINDOWS_2003_XP64,
WINDOWS_VISTA,
WINDOWS_7,
WINDOWS_8
};
/* The following is only available on Vista and later */
#if (_WIN32_WINNT >= 0x0600)
static HRESULT (WINAPI *pSHCreateItemFromParsingName)(PCWSTR, IBindCtx*, REFIID, void **) = NULL;
#endif
#define INIT_VISTA_SHELL32 if (pSHCreateItemFromParsingName == NULL) { \
pSHCreateItemFromParsingName = (HRESULT (WINAPI *)(PCWSTR, IBindCtx*, REFIID, void **)) \
GetProcAddress(GetModuleHandleA("SHELL32"), "SHCreateItemFromParsingName"); \
}
#define IS_VISTA_SHELL32_AVAILABLE (pSHCreateItemFromParsingName != NULL)
// And this one is simply not available in MinGW32
static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPath)(PCWSTR pszPath) = NULL;
#define INIT_XP_SHELL32 if (pSHSimpleIDListFromPath == NULL) { \
pSHSimpleIDListFromPath = (LPITEMIDLIST (WINAPI *)(PCWSTR)) \
GetProcAddress(GetModuleHandleA("SHELL32"), "SHSimpleIDListFromPath"); \
}
/* Globals */
static HICON hMessageIcon = (HICON)INVALID_HANDLE_VALUE;
static char* szMessageText = NULL;
static char* szMessageTitle = NULL;
enum WindowsVersion nWindowsVersion = WINDOWS_UNSUPPORTED;
static HWND hBrowseEdit;
static WNDPROC pOrgBrowseWndproc;
HFONT hBoldFont = NULL;
/*
* Detect Windows version
*/
void DetectWindowsVersion(void)
{
OSVERSIONINFO OSVersion;
memset(&OSVersion, 0, sizeof(OSVERSIONINFO));
OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
nWindowsVersion = WINDOWS_UNSUPPORTED;
if ((GetVersionEx(&OSVersion) != 0) && (OSVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
if ((OSVersion.dwMajorVersion == 5) && (OSVersion.dwMinorVersion == 0)) {
nWindowsVersion = WINDOWS_2K;
} else if ((OSVersion.dwMajorVersion == 5) && (OSVersion.dwMinorVersion == 1)) {
nWindowsVersion = WINDOWS_XP;
} else if ((OSVersion.dwMajorVersion == 5) && (OSVersion.dwMinorVersion == 2)) {
nWindowsVersion = WINDOWS_2003_XP64;
} else if (OSVersion.dwMajorVersion == 6) {
if (OSVersion.dwBuildNumber < 7000) {
nWindowsVersion = WINDOWS_VISTA;
} else {
nWindowsVersion = WINDOWS_7;
}
} else if (OSVersion.dwMajorVersion >= 8) {
nWindowsVersion = WINDOWS_8;
}
}
}
/*
* String array manipulation
*/
void StrArrayCreate(StrArray* arr, size_t initial_size)
{
if (arr == NULL) return;
arr->Max = initial_size; arr->Index = 0;
arr->Table = (char**)calloc(arr->Max, sizeof(char*));
if (arr->Table == NULL)
uprintf("Could not allocate string array\n");
}
void StrArrayAdd(StrArray* arr, const char* str)
{
if ((arr == NULL) || (arr->Table == NULL))
return;
if (arr->Index == arr->Max) {
arr->Max *= 2;
arr->Table = (char**)realloc(arr->Table, arr->Max*sizeof(char*));
if (arr->Table == NULL) {
uprintf("Could not reallocate string array\n");
return;
}
}
arr->Table[arr->Index] = safe_strdup(str);
if (arr->Table[arr->Index++] == NULL) {
uprintf("Could not store string in array\n");
}
}
void StrArrayClear(StrArray* arr)
{
size_t i;
if ((arr == NULL) || (arr->Table == NULL))
return;
for (i=0; i<arr->Index; i++) {
safe_free(arr->Table[i]);
}
arr->Index = 0;
}
void StrArrayDestroy(StrArray* arr)
{
StrArrayClear(arr);
safe_free(arr->Table);
}
/*
* 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);
SetFocus(hBrowseEdit);
// On XP, BFFM_SETSELECTION can't be used with a Unicode Path in SendMessageW
// or a pidl (at least with MinGW) => must use SendMessageA
if (nWindowsVersion <= WINDOWS_XP) {
SendMessageLU(hDlg, BFFM_SETSELECTION, (WPARAM)TRUE, szFolderPath);
} else {
// On Windows 7, MinGW only properly selects the specified folder when using a pidl
wpath = utf8_to_wchar(szFolderPath);
pidl = (*pSHSimpleIDListFromPath)(wpath);
safe_free(wpath);
// NB: see http://connect.microsoft.com/VisualStudio/feedback/details/518103/bffm-setselection-does-not-work-with-shbrowseforfolder-on-windows-7
// for details as to why we send BFFM_SETSELECTION twice.
SendMessageW(hDlg, BFFM_SETSELECTION, (WPARAM)FALSE, (LPARAM)pidl);
Sleep(100);
PostMessageW(hDlg, BFFM_SETSELECTION, (WPARAM)FALSE, (LPARAM)pidl);
}
break;
case BFFM_SELCHANGED:
// Update the status
if (SHGetPathFromIDListU((LPITEMIDLIST)lParam, dir)) {
SendMessageLU(hDlg, BFFM_SETSTATUSTEXT, 0, dir);
SetWindowTextU(hBrowseEdit, dir);
}
break;
}
return 0;
}
/*
* Browse for a folder and update the folder edit box
* Will use the newer IFileOpenDialog if *compiled* for Vista or later
*/
void BrowseForFolder(void) {
BROWSEINFOW bi;
LPITEMIDLIST pidl;
#if (_WIN32_WINNT >= 0x0600) // Vista and later
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;
// Even if we have Vista support with the compiler,
// it does not mean we have the Vista API available
INIT_VISTA_SHELL32;
if (IS_VISTA_SHELL32_AVAILABLE) {
hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC,
&IID_IFileOpenDialog, (LPVOID)&pfod);
if (FAILED(hr)) {
uprintf("CoCreateInstance for FileOpenDialog failed: error %X", hr);
pfod = NULL; // Just in case
goto fallback;
}
hr = pfod->lpVtbl->SetOptions(pfod, FOS_PICKFOLDERS);
if (FAILED(hr)) {
uprintf("Failed to set folder option for FileOpenDialog: error %X", hr);
goto fallback;
}
// Set the initial folder (if the path is invalid, will simply use last)
wpath = utf8_to_wchar(szFolderPath);
// The new IFileOpenDialog makes us split the path
fname = NULL;
if ((wpath != NULL) && (wcslen(wpath) >= 1)) {
for (i=wcslen(wpath)-1; i!=0; i--) {
if (wpath[i] == L'\\') {
wpath[i] = 0;
fname = &wpath[i+1];
break;
}
}
}
hr = (*pSHCreateItemFromParsingName)(wpath, NULL, &IID_IShellItem, (LPVOID)&si_path);
if (SUCCEEDED(hr)) {
if (wpath != NULL) {
hr = pfod->lpVtbl->SetFolder(pfod, si_path);
}
if (fname != NULL) {
hr = pfod->lpVtbl->SetFileName(pfod, fname);
}
}
safe_free(wpath);
hr = pfod->lpVtbl->Show(pfod, hMainDialog);
if (SUCCEEDED(hr)) {
hr = pfod->lpVtbl->GetResult(pfod, &psi);
if (SUCCEEDED(hr)) {
psi->lpVtbl->GetDisplayName(psi, SIGDN_FILESYSPATH, &wpath);
tmp_path = wchar_to_utf8(wpath);
CoTaskMemFree(wpath);
if (tmp_path == NULL) {
uprintf("Could not convert path");
} else {
safe_strcpy(szFolderPath, MAX_PATH, tmp_path);
safe_free(tmp_path);
}
} else {
uprintf("Failed to set folder option for FileOpenDialog: error %X", hr);
}
} else if ((hr & 0xFFFF) != ERROR_CANCELLED) {
// If it's not a user cancel, assume the dialog didn't show and fallback
uprintf("could not show FileOpenDialog: error %X", hr);
goto fallback;
}
pfod->lpVtbl->Release(pfod);
return;
}
fallback:
if (pfod != NULL) {
pfod->lpVtbl->Release(pfod);
}
#endif
INIT_XP_SHELL32;
memset(&bi, 0, sizeof(BROWSEINFOW));
bi.hwndOwner = hMainDialog;
bi.lpszTitle = L"Please select the installation folder:";
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);
}
}
/*
* Return the UTF8 path of a file selected through a load or save dialog
* Will use the newer IFileOpenDialog if *compiled* for Vista or later
* All string parameters are UTF-8
*/
char* FileDialog(BOOL save, char* path, char* filename, char* ext, char* ext_desc)
{
DWORD tmp;
OPENFILENAMEA ofn;
char selected_name[MAX_PATH];
char* ext_string = NULL;
size_t i, ext_strlen;
BOOL r;
char* filepath = NULL;
#if (_WIN32_WINNT >= 0x0600) // Vista and later
HRESULT hr = FALSE;
IFileDialog *pfd = NULL;
IShellItem *psiResult;
COMDLG_FILTERSPEC filter_spec[2];
char* ext_filter;
wchar_t *wpath = NULL, *wfilename = NULL;
IShellItem *si_path = NULL; // Automatically freed
INIT_VISTA_SHELL32;
if (IS_VISTA_SHELL32_AVAILABLE) {
// Setup the file extension filter table
ext_filter = (char*)malloc(strlen(ext)+3);
if (ext_filter != NULL) {
safe_sprintf(ext_filter, strlen(ext)+3, "*.%s", ext);
filter_spec[0].pszSpec = utf8_to_wchar(ext_filter);
safe_free(ext_filter);
filter_spec[0].pszName = utf8_to_wchar(ext_desc);
filter_spec[1].pszSpec = L"*.*";
filter_spec[1].pszName = L"All files";
}
hr = CoCreateInstance(save?&CLSID_FileSaveDialog:&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC,
&IID_IFileDialog, (LPVOID)&pfd);
if (FAILED(hr)) {
uprintf("CoCreateInstance for FileOpenDialog failed: error %X\n", hr);
pfd = NULL; // Just in case
goto fallback;
}
// Set the file extension filters
pfd->lpVtbl->SetFileTypes(pfd, 2, filter_spec);
// Set the default directory
wpath = utf8_to_wchar(path);
hr = (*pSHCreateItemFromParsingName)(wpath, NULL, &IID_IShellItem, (LPVOID) &si_path);
if (SUCCEEDED(hr)) {
pfd->lpVtbl->SetFolder(pfd, si_path);
}
safe_free(wpath);
// Set the default filename
wfilename = utf8_to_wchar(filename);
if (wfilename != NULL) {
pfd->lpVtbl->SetFileName(pfd, wfilename);
}
// Display the dialog
hr = pfd->lpVtbl->Show(pfd, hMainDialog);
// Cleanup
safe_free(wfilename);
safe_free(filter_spec[0].pszSpec);
safe_free(filter_spec[0].pszName);
if (SUCCEEDED(hr)) {
// Obtain the result of the user's interaction with the dialog.
hr = pfd->lpVtbl->GetResult(pfd, &psiResult);
if (SUCCEEDED(hr)) {
hr = psiResult->lpVtbl->GetDisplayName(psiResult, SIGDN_FILESYSPATH, &wpath);
if (SUCCEEDED(hr)) {
filepath = wchar_to_utf8(wpath);
CoTaskMemFree(wpath);
}
psiResult->lpVtbl->Release(psiResult);
}
} else if ((hr & 0xFFFF) != ERROR_CANCELLED) {
// If it's not a user cancel, assume the dialog didn't show and fallback
uprintf("could not show FileOpenDialog: error %X\n", hr);
goto fallback;
}
pfd->lpVtbl->Release(pfd);
return filepath;
}
fallback:
if (pfd != NULL) {
pfd->lpVtbl->Release(pfd);
}
#endif
memset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hMainDialog;
// File name
safe_strcpy(selected_name, MAX_PATH, filename);
ofn.lpstrFile = selected_name;
ofn.nMaxFile = MAX_PATH;
// Set the file extension filters
ext_strlen = strlen(ext_desc) + 2*strlen(ext) + sizeof(" (*.)\0*.\0All Files (*.*)\0*.*\0\0");
ext_string = (char*)malloc(ext_strlen);
safe_sprintf(ext_string, ext_strlen, "%s (*.%s)\r*.%s\rAll Files (*.*)\r*.*\r\0", ext_desc, ext, ext);
// Microsoft could really have picked a better delimiter!
for (i=0; i<ext_strlen; i++) {
if (ext_string[i] == '\r') {
ext_string[i] = 0;
}
}
ofn.lpstrFilter = ext_string;
// Initial dir
ofn.lpstrInitialDir = path;
ofn.Flags = OFN_OVERWRITEPROMPT;
// Show Dialog
if (save) {
r = GetSaveFileNameU(&ofn);
} else {
r = GetOpenFileNameU(&ofn);
}
if (r) {
filepath = safe_strdup(selected_name);
} else {
tmp = CommDlgExtendedError();
if (tmp != 0) {
uprintf("Could not selected file for %s. Error %X\n", save?"save":"open", tmp);
}
}
safe_free(ext_string);
return filepath;
}
void CreateBoldFont(HDC dc) {
TEXTMETRIC tm;
LOGFONT lf;
if (hBoldFont != NULL)
return;
GetTextMetrics(dc, &tm);
lf.lfHeight = tm.tmHeight+1;
lf.lfWidth = tm.tmAveCharWidth+1;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfWeight = FW_BOLD;
lf.lfItalic = tm.tmItalic;
lf.lfUnderline = FALSE;
lf.lfStrikeOut = tm.tmStruckOut;
lf.lfCharSet = tm.tmCharSet;
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lf.lfQuality = DEFAULT_QUALITY;
lf.lfPitchAndFamily = tm.tmPitchAndFamily;
GetTextFace(dc, LF_FACESIZE, lf.lfFaceName);
hBoldFont = CreateFontIndirect(&lf);
}
/*
* Create the application status bar
*/
void CreateStatusBar(void)
{
RECT rect;
int edge[2];
// Create the status bar.
hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | WS_VISIBLE,
0, 0, 0, 0, hMainDialog, (HMENU)IDC_STATUS, hMainInstance, NULL);
// Create 2 status areas
GetClientRect(hMainDialog, &rect);
edge[0] = rect.right - (int)(58.0f*fScale);
edge[1] = rect.right;
SendMessage(hStatus, SB_SETPARTS, (WPARAM) 2, (LPARAM)&edge);
}
/*
* Center a dialog with regards to the main application Window or the desktop
*/
void CenterDialog(HWND hDlg)
{
POINT Point;
HWND hParent;
RECT DialogRect;
RECT ParentRect;
int nWidth;
int nHeight;
// Get the size of the dialog box.
GetWindowRect(hDlg, &DialogRect);
// Get the parent
hParent = GetParent(hDlg);
if (hParent == NULL) {
hParent = GetDesktopWindow();
}
GetClientRect(hParent, &ParentRect);
// Calculate the height and width of the current dialog
nWidth = DialogRect.right - DialogRect.left;
nHeight = DialogRect.bottom - DialogRect.top;
// Find the center point and convert to screen coordinates.
Point.x = (ParentRect.right - ParentRect.left) / 2;
Point.y = (ParentRect.bottom - ParentRect.top) / 2;
ClientToScreen(hParent, &Point);
// Calculate the new x, y starting point.
Point.x -= nWidth / 2;
Point.y -= nHeight / 2 + 35;
// Move the window.
MoveWindow(hDlg, Point.x, Point.y, nWidth, nHeight, FALSE);
}
/*
* License callback
*/
INT_PTR CALLBACK LicenseCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_INITDIALOG:
CenterDialog(hDlg);
SetDlgItemTextA(hDlg, IDC_LICENSE_TEXT, gplv3);
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case IDCANCEL:
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)
{
switch (message) {
case WM_INITDIALOG:
CenterDialog(hDlg);
SetDlgItemTextA(hDlg, IDC_ABOUT_COPYRIGHTS, additional_copyrights);
break;
case WM_CTLCOLORSTATIC:
if ((HWND)lParam == GetDlgItem(hDlg, IDC_RUFUS_BOLD)) {
CreateBoldFont((HDC)wParam);
SetBkMode((HDC)wParam, TRANSPARENT);
SelectObject((HDC)wParam, hBoldFont);
return (INT_PTR)CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
}
break;
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code) {
case NM_CLICK:
case NM_RETURN:
switch (LOWORD(wParam)) {
case IDC_ABOUT_RUFUS_URL:
ShellExecuteA(hDlg, "open", RUFUS_URL, NULL, NULL, SW_SHOWNORMAL);
break;
case IDC_ABOUT_BUG_URL:
ShellExecuteA(hDlg, "open", BUG_URL, NULL, NULL, SW_SHOWNORMAL);
break;
}
break;
}
break;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
case IDC_ABOUT_LICENSE:
DialogBoxA(hMainInstance, MAKEINTRESOURCEA(IDD_LICENSE), hDlg, LicenseCallback);
break;
}
break;
}
return (INT_PTR)FALSE;
}
INT_PTR CreateAboutBox(void)
{
return DialogBoxA(hMainInstance, MAKEINTRESOURCEA(IDD_ABOUTBOX), hMainDialog, AboutCallback);
}
/*
* We use our own MessageBox for notifications to have greater control (center, no close button, etc)
*/
INT_PTR CALLBACK NotificationCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT loc;
int i;
// Prevent resizing
static LRESULT disabled[9] = { HTLEFT, HTRIGHT, HTTOP, HTBOTTOM, HTSIZE,
HTTOPLEFT, HTTOPRIGHT, HTBOTTOMLEFT, HTBOTTOMRIGHT };
static HBRUSH white_brush, separator_brush;
switch (message) {
case WM_INITDIALOG:
white_brush = CreateSolidBrush(WHITE);
separator_brush = CreateSolidBrush(SEPARATOR_GREY);
CenterDialog(hDlg);
// Change the default icon
if (Static_SetIcon(GetDlgItem(hDlg, IDC_NOTIFICATION_ICON), hMessageIcon) == 0) {
uprintf("could not set dialog icon");
}
// Set the dialog title
if (szMessageTitle != NULL) {
SetWindowTextA(hDlg, szMessageTitle);
}
// Set the control text
if (szMessageText != NULL) {
SetWindowTextA(GetDlgItem(hDlg, IDC_NOTIFICATION_TEXT), szMessageText);
}
return (INT_PTR)TRUE;
case WM_CTLCOLORSTATIC:
// Change the background colour for static text and icon
SetBkMode((HDC)wParam, TRANSPARENT);
if ((HWND)lParam == GetDlgItem(hDlg, IDC_NOTIFICATION_LINE)) {
return (INT_PTR)separator_brush;
}
return (INT_PTR)white_brush;
case WM_NCHITTEST:
// Check coordinates to prevent resize actions
loc = DefWindowProc(hDlg, message, wParam, lParam);
for(i = 0; i < 9; i++) {
if (loc == disabled[i]) {
return (INT_PTR)TRUE;
}
}
return (INT_PTR)FALSE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
/*
* Display a custom notification
*/
BOOL Notification(int type, char* title, char* format, ...)
{
va_list args;
szMessageText = (char*)malloc(MAX_PATH);
if (szMessageText == NULL) return FALSE;
szMessageTitle = title;
va_start(args, format);
safe_vsnprintf(szMessageText, MAX_PATH-1, format, args);
va_end(args);
szMessageText[MAX_PATH-1] = 0;
switch(type) {
case MSG_WARNING:
hMessageIcon = LoadIcon(NULL, IDI_WARNING);
break;
case MSG_ERROR:
hMessageIcon = LoadIcon(NULL, IDI_ERROR);
break;
case MSG_INFO:
default:
hMessageIcon = LoadIcon(NULL, IDI_INFORMATION);
break;
}
DialogBox(hMainInstance, MAKEINTRESOURCE(IDD_NOTIFICATION), hMainDialog, NotificationCallback);
safe_free(szMessageText);
return TRUE;
}
static struct {
HWND hTip;
WNDPROC original_proc;
LPWSTR wstring;
} ttlist[MAX_TOOLTIPS] = { {0} };
INT_PTR CALLBACK TooltipCallback(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
LPNMTTDISPINFOW lpnmtdi;
int i = MAX_TOOLTIPS;
// Make sure we have an original proc
for (i=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == hDlg) break;
}
if (i == MAX_TOOLTIPS) {
return (INT_PTR)FALSE;
}
switch (message)
{
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code) {
case TTN_GETDISPINFOW:
lpnmtdi = (LPNMTTDISPINFOW)lParam;
lpnmtdi->lpszText = ttlist[i].wstring;
SendMessage(hDlg, TTM_SETMAXTIPWIDTH, 0, 300);
return (INT_PTR)TRUE;
}
break;
}
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
*/
2011-11-24 23:49:42 +00:00
HWND CreateTooltip(HWND hControl, const char* message, int duration)
{
TOOLINFOW toolInfo = {0};
int i;
if ( (hControl == NULL) || (message == NULL) ) {
return (HWND)NULL;
}
// Find an empty slot
for (i=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == NULL) break;
}
if (i == MAX_TOOLTIPS) {
uprintf("Maximum number of tooltips reached\n");
return (HWND)NULL;
}
// Create the tooltip window
ttlist[i].hTip = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hMainDialog, NULL,
hMainInstance, NULL);
if (ttlist[i].hTip == NULL) {
return (HWND)NULL;
}
// Subclass the tooltip to handle multiline
ttlist[i].original_proc = (WNDPROC)SetWindowLongPtr(ttlist[i].hTip, GWLP_WNDPROC, (LONG_PTR)TooltipCallback);
// Set the string to display (can be multiline)
ttlist[i].wstring = utf8_to_wchar(message);
// Set tooltip duration (ms)
PostMessage(ttlist[i].hTip, TTM_SETDELAYTIME, (WPARAM)TTDT_AUTOPOP, (LPARAM)duration);
// Associate the tooltip to the control
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.hwnd = ttlist[i].hTip; // Set to the tooltip itself to ease up subclassing
toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
toolInfo.uId = (UINT_PTR)hControl;
toolInfo.lpszText = LPSTR_TEXTCALLBACKW;
SendMessageW(ttlist[i].hTip, TTM_ADDTOOLW, 0, (LPARAM)&toolInfo);
return ttlist[i].hTip;
}
void DestroyTooltip(HWND hWnd)
{
int i;
if (hWnd == NULL) return;
for (i=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == hWnd) break;
}
if (i == MAX_TOOLTIPS) return;
DestroyWindow(hWnd);
safe_free(ttlist[i].wstring);
ttlist[i].original_proc = NULL;
ttlist[i].hTip = NULL;
}
void DestroyAllTooltips(void)
{
int i;
for (i=0; i<MAX_TOOLTIPS; i++) {
if (ttlist[i].hTip == NULL) continue;
DestroyWindow(ttlist[i].hTip);
safe_free(ttlist[i].wstring);
}
}
/* Compute the width of a device list entry */
LONG GetEntryWidth(HWND hDropDown, const char *entry)
{
HDC hDC;
HFONT hFont, hDefFont;
SIZE size;
WCHAR* wentry = NULL;
int len;
hDC = GetDC(hDropDown);
hFont = (HFONT)SendMessage(hDropDown, WM_GETFONT, 0, 0);
if (hFont != NULL)
hDefFont = (HFONT)SelectObject(hDC, hFont);
wentry = utf8_to_wchar(entry);
len = (int)wcslen(wentry)+1;
GetTextExtentPoint32W(hDC, wentry, len, &size);
if (hFont != NULL)
SelectObject(hDC, hDefFont);
ReleaseDC(hDropDown, hDC);
free(wentry);
return size.cx;
}
/*
* Windows 7 taskbar icon handling (progress bar overlay, etc)
* Some platforms don't have these, so we redefine
*/
typedef enum MY_STPFLAG
{
MY_STPF_NONE = 0,
MY_STPF_USEAPPTHUMBNAILALWAYS = 0x1,
MY_STPF_USEAPPTHUMBNAILWHENACTIVE = 0x2,
MY_STPF_USEAPPPEEKALWAYS = 0x4,
MY_STPF_USEAPPPEEKWHENACTIVE = 0x8
} MY_STPFLAG;
typedef enum MY_THUMBBUTTONMASK
{
MY_THB_BITMAP = 0x1,
MY_THB_ICON = 0x2,
MY_THB_TOOLTIP = 0x4,
MY_THB_FLAGS = 0x8
} MY_THUMBBUTTONMASK;
typedef enum MY_THUMBBUTTONFLAGS
{
MY_THBF_ENABLED = 0,
MY_THBF_DISABLED = 0x1,
MY_THBF_DISMISSONCLICK = 0x2,
MY_THBF_NOBACKGROUND = 0x4,
MY_THBF_HIDDEN = 0x8,
MY_THBF_NONINTERACTIVE = 0x10
} MY_THUMBBUTTONFLAGS;
typedef struct MY_THUMBBUTTON
{
MY_THUMBBUTTONMASK dwMask;
UINT iId;
UINT iBitmap;
HICON hIcon;
WCHAR szTip[260];
MY_THUMBBUTTONFLAGS dwFlags;
} MY_THUMBBUTTON;
/*
typedef enum MY_TBPFLAG
{
TASKBAR_NOPROGRESS = 0,
TASKBAR_INDETERMINATE = 0x1,
TASKBAR_NORMAL = 0x2,
TASKBAR_ERROR = 0x4,
TASKBAR_PAUSED = 0x8
} MY_TBPFLAG;
*/
#pragma push_macro("INTERFACE")
#undef INTERFACE
#define INTERFACE my_ITaskbarList3
DECLARE_INTERFACE_(my_ITaskbarList3, IUnknown) {
STDMETHOD (QueryInterface) (THIS_ REFIID riid, LPVOID *ppvObj) PURE;
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
STDMETHOD_(ULONG, Release) (THIS) PURE;
STDMETHOD (HrInit) (THIS) PURE;
STDMETHOD (AddTab) (THIS_ HWND hwnd) PURE;
STDMETHOD (DeleteTab) (THIS_ HWND hwnd) PURE;
STDMETHOD (ActivateTab) (THIS_ HWND hwnd) PURE;
STDMETHOD (SetActiveAlt) (THIS_ HWND hwnd) PURE;
STDMETHOD (MarkFullscreenWindow) (THIS_ HWND hwnd, int fFullscreen) PURE;
STDMETHOD (SetProgressValue) (THIS_ HWND hwnd, ULONGLONG ullCompleted, ULONGLONG ullTotal) PURE;
STDMETHOD (SetProgressState) (THIS_ HWND hwnd, TASKBAR_PROGRESS_FLAGS tbpFlags) PURE;
STDMETHOD (RegisterTab) (THIS_ HWND hwndTab,HWND hwndMDI) PURE;
STDMETHOD (UnregisterTab) (THIS_ HWND hwndTab) PURE;
STDMETHOD (SetTabOrder) (THIS_ HWND hwndTab, HWND hwndInsertBefore) PURE;
STDMETHOD (SetTabActive) (THIS_ HWND hwndTab, HWND hwndMDI, DWORD dwReserved) PURE;
STDMETHOD (ThumbBarAddButtons) (THIS_ HWND hwnd, UINT cButtons, MY_THUMBBUTTON* pButton) PURE;
STDMETHOD (ThumbBarUpdateButtons) (THIS_ HWND hwnd, UINT cButtons, MY_THUMBBUTTON* pButton) PURE;
STDMETHOD (ThumbBarSetImageList) (THIS_ HWND hwnd, HIMAGELIST himl) PURE;
STDMETHOD (SetOverlayIcon) (THIS_ HWND hwnd, HICON hIcon, LPCWSTR pszDescription) PURE;
STDMETHOD (SetThumbnailTooltip) (THIS_ HWND hwnd, LPCWSTR pszTip) PURE;
STDMETHOD (SetThumbnailClip) (THIS_ HWND hwnd, RECT *prcClip) PURE;
};
const IID my_IID_ITaskbarList3 =
{ 0xea1afb91, 0x9e28, 0x4b86, { 0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf } };
const IID my_CLSID_TaskbarList =
{ 0x56fdf344, 0xfd6d, 0x11d0, { 0x95, 0x8a ,0x0, 0x60, 0x97, 0xc9, 0xa0 ,0x90 } };
static my_ITaskbarList3* ptbl = NULL;
BOOL CreateTaskbarList(void)
{
HRESULT hr;
// Create the taskbar icon progressbar
hr = CoCreateInstance(&my_CLSID_TaskbarList, NULL, CLSCTX_ALL, &my_IID_ITaskbarList3, (LPVOID)&ptbl);
if (FAILED(hr)) {
uprintf("CoCreateInstance for TaskbarList failed: error %X", hr);
ptbl = NULL;
return FALSE;
}
return TRUE;
}
BOOL SetTaskbarProgressState(TASKBAR_PROGRESS_FLAGS tbpFlags)
{
if (ptbl == NULL)
return FALSE;
return !FAILED(ptbl->lpVtbl->SetProgressState(ptbl, hMainDialog, tbpFlags));
}
BOOL SetTaskbarProgressValue(ULONGLONG ullCompleted, ULONGLONG ullTotal)
{
if (ptbl == NULL)
return FALSE;
return !FAILED(ptbl->lpVtbl->SetProgressValue(ptbl, hMainDialog, ullCompleted, ullTotal));
}
#pragma pop_macro("INTERFACE")