Files
2025-09-18 17:55:52 -04:00

427 lines
10 KiB
C++

#include "StdAfx.h"
#include "ProgressDialog.h"
#include "commonresourceconstants.h"
#include "SharedHelpers.h"
#include <commctrl.h>
#pragma comment (lib, "comctl32.lib")
#include "format_string.h"
#include <Psapi.h>
// can't use these on windows xp :(
//#include <dwmapi.h>
//#pragma comment (lib, "dwmapi.lib")
#define IDT_TIMER 1
const static int WM_MARQUEE = WM_USER + 101; // See Q196026
const static int WM_PROGRESS = WM_USER + 102; // See Q196026
static HICON icon;
WNDPROC OldButtonProc;
HWND CProgressDialog::CreateTextHelper(HWND hWndParent, HFONT font, int x, int y, int width, int height, bool hidden)
{
DWORD style = WS_CHILD | SS_CENTER;
if (!hidden)
style |= WS_VISIBLE;
HWND hWndText = CreateWindowEx(0, L"Static", L"", style,
x, y, width, height,
hWndParent, NULL, GetModuleHandle(NULL), NULL);
SendMessage(hWndText, WM_SETFONT, (LPARAM)font, TRUE);
return hWndText;
}
LRESULT CALLBACK CancelButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static bool tracking = false;
switch(uMsg)
{
case WM_MOUSEMOVE:
{
if (!tracking)
{
HWND hWndCancelBnt = GetDlgItem(hWnd, IDCANCEL);
TRACKMOUSEEVENT tme;
tme.cbSize= sizeof(tme);
tme.dwFlags= TME_HOVER | TME_LEAVE;
tme.hwndTrack= hWnd; //handle of window you want the mouse over message for.
tme.dwHoverTime= 50;
tracking = _TrackMouseEvent(&tme);
}
}
break;
case WM_MOUSEHOVER:
{
CProgressDialog* dialog = (CProgressDialog*)GetWindowLongPtr(hWnd, GWL_USERDATA);
if (!dialog->isSuccessPromptShown())
SendMessage(hWnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)dialog->getCancelBitmap(true));
}
break;
case WM_MOUSELEAVE:
{
CProgressDialog* dialog = (CProgressDialog*)GetWindowLongPtr(hWnd, GWL_USERDATA);
if (!dialog->isSuccessPromptShown())
SendMessage(hWnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)dialog->getCancelBitmap(false));
tracking = false;
}
break;
default:
return CallWindowProc(OldButtonProc, hWnd, uMsg, wParam, lParam);
}
return true;
}
LRESULT CALLBACK CProgressDialog::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HBRUSH hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
static CProgressDialog* dialog = NULL;
switch (uMsg)
{
case WM_CREATE:
{
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
dialog = static_cast<CProgressDialog*>(pCreate->lpCreateParams);
//OnCreate(hwnd);
::SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) icon);
::SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) icon);
}
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDCANCEL)
{
SendMessage(hwnd, WM_CLOSE, 0, 0);
if (dialog && !dialog->closeCallback.empty())
dialog->closeCallback();
}
break;
case WM_NCCALCSIZE:
return 0;
case WM_CTLCOLORSTATIC:
{
HDC hdcStatic = (HDC)wParam;
SetTextColor(hdcStatic, RGB(25, 25, 25));
SetBkMode(hdcStatic, TRANSPARENT);
return (LONG)hbrBackground;
}
break;
case WM_DESTROY:
// This message could be sent if dialog was closed from taskbar and bypassed CloseDialog(),
// so call the closeCallback to indicate it's a user cancel
if (dialog && !dialog->dialogClosing && !dialog->closeCallback.empty())
dialog->closeCallback();
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, hbrBackground);
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(184, 184, 184));
SelectObject(hdc, hPen);
RECT rect;
GetWindowRect(hwnd, &rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Rectangle(hdc, 0, 0, width, height);
DeleteObject(hPen);
GetStockObject(DC_PEN);
EndPaint(hwnd, &ps);
}
return 0;
case WM_ACTIVATE:
InvalidateRect(hwnd, 0, NULL);
break;
case WM_PROGRESS:
{
HWND prog = ::GetDlgItem(hwnd, IDC_PROGRESS);
DWORD style = GetWindowLongPtr(prog, GWL_STYLE);
if ((style & PBS_MARQUEE) == 0)
::SendMessage(prog, PBM_SETPOS, wParam, 0);
}
break;
case WM_MARQUEE:
{
HWND prog = ::GetDlgItem(hwnd, IDC_PROGRESS);
DWORD style = GetWindowLongPtr(prog, GWL_STYLE);
if (wParam)
{
if ((style & PBS_MARQUEE) == 0)
{
::SetWindowLongPtr(prog, GWL_STYLE, style | PBS_MARQUEE);
::SendMessage(prog, PBM_SETMARQUEE, TRUE, 0);
}
}
else
{
if ((style & PBS_MARQUEE) != 0)
{
::SendMessage(prog, PBM_SETMARQUEE, FALSE, 0);
::SetWindowLongPtr(prog, GWL_STYLE, style & ~PBS_MARQUEE);
}
}
}
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
CProgressDialog::CProgressDialog(HINSTANCE hInstance) : hInstance(hInstance), dialogClosing(false)
{
icon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_BOOTSTRAPPER));
bmpCancel = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BTN_CANCEL));
bmpCancelOn = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BTN_CANCEL_ON));
bmpLogo = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_LOGO));
}
CProgressDialog::~CProgressDialog(void)
{
CloseDialog();
}
void CProgressDialog::InitDialog(WNDPROC wndProc)
{
INITCOMMONCONTROLSEX c;
c.dwSize = sizeof(INITCOMMONCONTROLSEX);
c.dwICC = ICC_PROGRESS_CLASS;
::InitCommonControlsEx(&c);
const wchar_t CLASS_NAME[] = _T("MAINDIALOG");
WNDCLASS wc = {};
wc.lpfnWndProc = wndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wc);
int sizeX = 500;
int sizeY = 320;
int posX = (::GetSystemMetrics(SM_CXSCREEN) - sizeX) / 2;
int posY = (::GetSystemMetrics(SM_CYSCREEN) - sizeY) / 2;
hWndDialog = CreateWindowEx(
0,
CLASS_NAME,
L"",
WS_POPUP | WS_THICKFRAME,
posX, posY, sizeX, sizeY,
NULL,
NULL,
hInstance,
(LPVOID)this
);
OldButtonProc = (WNDPROC)SetWindowLongPtr(hWndButton, GWL_WNDPROC, (LONG) CancelButtonProc);
SetWindowLongPtr(hWndButton, GWL_USERDATA, (LONG)this);
}
void CProgressDialog::OnCreate(HWND hWnd)
{
RECT rect;
GetWindowRect(hWnd, &rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
// can't use these on windows xp
//MARGINS borderless = {1,1,1,1};
//HRESULT hr = DwmExtendFrameIntoClientArea(hWnd, &borderless);
// set up logo
int logoSize = 90;
HWND hPic = CreateWindowEx(0, WC_STATIC, L"", WS_CHILD | WS_VISIBLE | SS_BITMAP,
width/2-logoSize/2, 70, logoSize, logoSize,
hWnd, NULL, GetModuleHandle(NULL), NULL);
SendMessage(hPic, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)bmpLogo);
// set up text
int textWidth = width - 10;
HDC hdc = GetDC(NULL);
long fontHight = -MulDiv(11, GetDeviceCaps(hdc, LOGPIXELSY), 72);
fontMsg = CreateFont(fontHight, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _T("Tahoma"));
hWndText = CreateTextHelper(hWnd, fontMsg, 5, 200, textWidth, 20, false);
// set up progress bar
hWndProgressBar = CreateWindowEx(0, PROGRESS_CLASS, NULL, WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
24, 242, 452, 20,
hWnd, (HMENU)IDC_PROGRESS, GetModuleHandle(NULL), NULL);
SendMessage(hWndProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
// set up button, start hidden
int bntWidth = 130;
int bntHeight = 44;
hWndButton = CreateWindowEx(0, L"Button", L"", WS_CHILD | BS_FLAT | BS_BITMAP,
width/2-bntWidth/2, height - 55, bntWidth, bntHeight, hWnd, (HMENU)IDCANCEL, GetModuleHandle(NULL), NULL);
HBITMAP bntBmp = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BTN_CANCEL));
SendMessage(hWndButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)bntBmp);
DeleteObject(bntBmp);
ReleaseDC(hWnd, hdc);
}
VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
// try find Player or Studio process
DWORD process_id_array[1024];
DWORD bytes_returned;
EnumProcesses(process_id_array, 1024*sizeof(DWORD), &bytes_returned);
const DWORD num_processes = bytes_returned/sizeof(DWORD);
for(DWORD i=0; i<num_processes; i++)
{
CHandle hProcess(OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, process_id_array[i] ));
TCHAR image_name[1024];
if(GetProcessImageFileName(hProcess,image_name,1024 ))
{
image_name[1023] = 0;
std::wstring s = image_name;
if (s.find(_T(PLAYEREXENAME)) != std::wstring::npos ||
s.find(_T(STUDIOQTEXENAME)) != std::wstring::npos)
{
// close the dialog via WM_COMMAND handler in WindowProc
SendMessage(hwnd, WM_CLOSE, 0, 0);
break;
}
}
}
}
void CProgressDialog::ShowSuccessPrompt()
{
showSuccessPrompt = true;
::ShowWindow(hWndButton, SW_HIDE);
::ShowWindow(hWndText, SW_HIDE);
::ShowWindow(hWndProgressBar, SW_HIDE);
// start timer to poll for launch of Player or Studio
SetTimer(hWndDialog, IDT_TIMER, 300, (TIMERPROC)TimerProc);
}
int CProgressDialog::MessageBox(LPCWSTR text, LPCWSTR caption, UINT uType)
{
if ( GetSilentMode() )
return 0;
return ::MessageBox(hWndDialog, text, caption, uType);
}
void CProgressDialog::ShowWindow()
{
if (GetSilentMode())
{
return;
}
::ShowWindow(hWndDialog, SW_SHOWNORMAL);
}
void CProgressDialog::CloseDialog(void)
{
dialogClosing = true;
KillTimer(hWndDialog, IDT_TIMER);
DeleteObject(bmpLogo);
DeleteObject(bmpCancel);
DeleteObject(bmpCancelOn);
DeleteObject(fontMsg);
DestroyWindow(hWndDialog);
}
void CProgressDialog::SetMessage(const char* message)
{
if (GetSilentMode())
{
return;
}
SendMessage(hWndText, WM_SETTEXT, NULL, (LPARAM)convert_s2w(std::string(message)).c_str());
}
void CProgressDialog::DisplayError(const char* message, const char* exceptionText)
{
std::string text = message;
if (exceptionText)
{
text += "\n\nDetails: ";
text += exceptionText;
}
MessageBox(convert_s2w(text).c_str(), _T("Watrbx"), MB_OK | MB_ICONERROR);
}
void CProgressDialog::FinalMessage(const char* message)
{
MessageBox(convert_s2w(message).c_str(), _T("Watrbx"), MB_OK);
}
void CProgressDialog::SetCancelEnabled(bool state)
{
if (GetSilentMode() || isSuccessPromptShown())
{
return;
}
HWND ctrl = ::GetDlgItem(hWndDialog, IDCANCEL);
::EnableWindow(ctrl, state ? TRUE : FALSE);
::ShowWindow(ctrl, state ? SW_SHOWNORMAL : SW_HIDE);
}
void CProgressDialog::SetCancelVisible(bool state)
{
if (GetSilentMode() || isSuccessPromptShown())
{
return;
}
HWND ctrl = ::GetDlgItem(hWndDialog, IDCANCEL);
::ShowWindow(ctrl, state ? TRUE : FALSE);
}
void CProgressDialog::SetMarquee(bool state)
{
if (GetSilentMode())
{
return;
}
SendMessage(hWndDialog, WM_MARQUEE, state ? TRUE : FALSE, 0);
}
void CProgressDialog::SetProgress(int percent)
{
if (GetSilentMode())
{
return;
}
SendMessage(hWndDialog, WM_PROGRESS, percent, 0);
}
HBITMAP CProgressDialog::getCancelBitmap(bool hover)
{
return hover ? bmpCancelOn : bmpCancel;
}