overte-JulianGro/launchers/win32/LauncherDlg.cpp
2022-08-07 14:01:42 +02:00

865 lines
33 KiB
C++

//
// LauncherDlg.cpp
//
// Created by Luis Cuenca on 6/5/2019.
// Copyright 2019 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "stdafx.h"
#include <regex>
#include "LauncherApp.h"
#include "LauncherDlg.h"
#include <propsys.h>
#include <propkey.h>
#include <d2d1.h>
#pragma comment(lib, "d2d1")
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
static int ACTION_FONT_SIZE = 40;
static int MESSAGE_FONT_SIZE = 19;
static int FIELDS_FONT_SIZE = 24;
static int BUTTON_FONT_SIZE = 25;
static int TERMS_FONT_SIZE = 17;
static int TROUBLE_FONT_SIZE = 14;
static COLORREF COLOR_GREY = RGB(120, 120, 120);
static COLORREF COLOR_BLACK= RGB(0, 0, 0);
static COLORREF COLOR_WHITE = RGB(255, 255, 255);
static COLORREF COLOR_LIGHTER_GREY = RGB(230, 230, 230);
static COLORREF COLOR_LIGHT_GREY = RGB(200, 200, 200);
static COLORREF COLOR_BLUE = RGB(50, 160, 200);
static CString FIRASANS_REGULAR = _T("FiraSans-Regular");
static CString FIRASANS_SEMIBOLD = _T("FiraSans-SemiBold");
static CString TROUBLE_URL = _T("https://www.highfidelity.com/hq-support");
static CString TERMS_URL = _T("https://www.highfidelity.com/termsofservice");
static int SPLASH_DURATION = 100;
CLauncherDlg::CLauncherDlg(CWnd* pParent)
: CDialog(IDD_LAUNCHER_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
EnableD2DSupport();
}
CLauncherDlg::~CLauncherDlg() {
theApp._manager.closeLog();
}
void CLauncherDlg::DoDataExchange(CDataExchange* pDX)
{
DDX_Control(pDX, IDC_BUTTON_NEXT, m_btnNext);
DDX_Control(pDX, IDC_TROUBLE_LINK, m_trouble_link);
DDX_Control(pDX, IDC_TERMS_LINK, m_terms_link);
DDX_Control(pDX, IDC_ORGNAME, m_orgname);
DDX_Control(pDX, IDC_USERNAME, m_username);
DDX_Control(pDX, IDC_PASSWORD, m_password);
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CLauncherDlg, CDialog)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_EN_SETFOCUS(IDC_ORGNAME, &CLauncherDlg::OnOrgEditChangeFocus)
ON_EN_SETFOCUS(IDC_USERNAME, &CLauncherDlg::OnUserEditChangeFocus)
ON_EN_SETFOCUS(IDC_PASSWORD, &CLauncherDlg::OnPassEditChangeFocus)
ON_BN_CLICKED(IDC_BUTTON_NEXT, &CLauncherDlg::OnNextClicked)
ON_BN_CLICKED(IDC_TROUBLE_LINK, &CLauncherDlg::OnTroubleClicked)
ON_BN_CLICKED(IDC_TERMS_LINK, &CLauncherDlg::OnTermsClicked)
ON_WM_CTLCOLOR()
ON_WM_DRAWITEM()
ON_WM_SETCURSOR()
END_MESSAGE_MAP()
// CLauncherDlg message handlers
BOOL CLauncherDlg::OnInitDialog() {
CDialog::OnInitDialog();
MarkWindowAsUnpinnable();
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
CFont editFont;
if (LauncherUtils::getFont(FIRASANS_REGULAR, FIELDS_FONT_SIZE, true, editFont)) {
m_orgname.SetFont(&editFont);
m_username.SetFont(&editFont);
m_password.SetFont(&editFont);
}
CFont buttonFont;
if (LauncherUtils::getFont(_T("FiraSans-Bold"), BUTTON_FONT_SIZE, true, buttonFont)) {
m_btnNext.SetFont(&editFont);
}
m_message_label = (CStatic *)GetDlgItem(IDC_MESSAGE_LABEL);
m_action_label = (CStatic *)GetDlgItem(IDC_ACTION_LABEL);
m_message2_label = (CStatic *)GetDlgItem(IDC_MESSAGE2_LABEL);
m_action2_label = (CStatic *)GetDlgItem(IDC_ACTION2_LABEL);
m_orgname_banner = (CStatic *)GetDlgItem(IDC_ORGNAME_BANNER);
m_username_banner = (CStatic *)GetDlgItem(IDC_USERNAME_BANNER);
m_password_banner = (CStatic *)GetDlgItem(IDC_PASSWORD_BANNER);
m_terms = (CStatic *)GetDlgItem(IDC_TERMS);
m_trouble = (CStatic *)GetDlgItem(IDC_TROUBLE);
m_voxel = (CStatic *)GetDlgItem(IDC_VOXEL);
m_progress = (CStatic *)GetDlgItem(IDC_PROGRESS);
m_version = (CStatic *)GetDlgItem(IDC_VERSION);
CString version;
version.Format(_T("V.%s"), theApp._manager.getLauncherVersion());
m_version->SetWindowTextW(version);
m_voxel->EnableD2DSupport();
m_progress->EnableD2DSupport();
m_pRenderTarget = GetRenderTarget();
SetTimer(1, 2, NULL);
return TRUE;
}
void CLauncherDlg::MarkWindowAsUnpinnable() {
HWND hwnd = AfxGetMainWnd()->m_hWnd;
IPropertyStore* pps;
HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr)) {
PROPVARIANT var;
var.vt = VT_BOOL;
var.boolVal = VARIANT_TRUE;
hr = pps->SetValue(PKEY_AppUserModel_PreventPinning, var);
pps->Release();
}
}
POINT CLauncherDlg::getMouseCoords(MSG* pMsg) {
POINT pos;
pos.x = (int)(short)LOWORD(pMsg->lParam);
pos.y = (int)(short)HIWORD(pMsg->lParam);
return pos;
}
BOOL CLauncherDlg::PreTranslateMessage(MSG* pMsg) {
switch (pMsg->message) {
case WM_KEYDOWN:
if (pMsg->wParam == 'A' && GetKeyState(VK_CONTROL) < 0) {
CWnd* wnd = GetFocus();
CWnd* myWnd = this->GetDlgItem(IDC_ORGNAME);
if (wnd && (wnd == this->GetDlgItem(IDC_ORGNAME) ||
wnd == this->GetDlgItem(IDC_USERNAME) ||
wnd == this->GetDlgItem(IDC_PASSWORD))) {
((CEdit*)wnd)->SetSel(0, -1);
}
return TRUE;
} else if (pMsg->wParam == VK_RETURN) {
OnNextClicked();
return TRUE;
} else if (pMsg->wParam == VK_ESCAPE) {
theApp._manager.onCancel();
}
break;
case WM_LBUTTONDOWN:
if (pMsg->hwnd == GetSafeHwnd()) {
_draggingWindow = true;
_dragOffset = getMouseCoords(pMsg);
SetCapture();
}
break;
case WM_LBUTTONUP:
if (_draggingWindow) {
ReleaseCapture();
_draggingWindow = false;
}
break;
case WM_MOUSEMOVE:
if (_draggingWindow) {
POINT pos = getMouseCoords(pMsg);
RECT windowRect;
GetWindowRect(&windowRect);
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
ClientToScreen(&pos);
MoveWindow(pos.x - _dragOffset.x, pos.y - _dragOffset.y, width, height, FALSE);
}
break;
default:
break;
}
return CDialog::PreTranslateMessage(pMsg);
}
void CLauncherDlg::setCustomDialog() {
LONG lStyle = GetWindowLong(GetSafeHwnd(), GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
SetWindowLong(GetSafeHwnd(), GWL_STYLE, lStyle);
LONG lExStyle = GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, lExStyle);
SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
void CLauncherDlg::OnPaint()
{
CPaintDC dc(this);
setCustomDialog();
CDialog::OnPaint();
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CLauncherDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CLauncherDlg::startProcess() {
if (theApp._manager.needsUpdate()) {
theApp._manager.addToLog(_T("Starting Process Update"));
setDrawDialog(DrawStep::DrawProcessUpdate);
} else {
theApp._manager.addToLog(_T("Starting Process Setup"));
setDrawDialog(DrawStep::DrawProcessSetup);
}
theApp._manager.addToLog(_T("Deleting download directory"));
CString downloadDir;
theApp._manager.getAndCreatePaths(LauncherManager::PathType::Download_Directory, downloadDir);
LauncherUtils::deleteDirectoryOnThread(downloadDir, [&](bool error) {
if (!error) {
if (!theApp._manager.isLoggedIn()) {
theApp._manager.addToLog(_T("Downloading Content"));
theApp._manager.downloadContent();
} else {
theApp._manager.addToLog(_T("Downloading App"));
theApp._manager.downloadApplication();
}
} else {
theApp._manager.addToLog(_T("Error deleting download directory."));
theApp._manager.setFailed(true);
}
});
}
BOOL CLauncherDlg::getHQInfo(const CString& orgname) {
CString hash;
CString lowerOrgName = orgname;
lowerOrgName.MakeLower();
LauncherUtils::hMac256(lowerOrgName, LAUNCHER_HMAC_SECRET, hash);
CString msg;
msg.Format(_T("Calculated hash: \"%s\" => \"%s\""), lowerOrgName, hash);
theApp._manager.addToLog(msg);
return theApp._manager.readOrganizationJSON(hash) == LauncherUtils::ResponseError::NoError;
}
afx_msg void CLauncherDlg::OnTroubleClicked() {
LauncherUtils::executeOnForeground(TROUBLE_URL, _T(""));
}
afx_msg void CLauncherDlg::OnTermsClicked() {
LauncherUtils::executeOnForeground(TERMS_URL, _T(""));
}
afx_msg void CLauncherDlg::OnNextClicked() {
if (_drawStep == DrawStep::DrawChoose) {
CString displayName;
m_username.GetWindowTextW(displayName);
theApp._manager.setDisplayName(displayName);
theApp._manager.addToLog(_T("Setting display name: " + displayName));
startProcess();
} else if (_drawStep == DrawStep::DrawError) {
theApp._manager.restartLauncher();
} else if (_drawStep == DrawStep::DrawLoginLogin ||
_drawStep == DrawStep::DrawLoginErrorCred ||
_drawStep == DrawStep::DrawLoginErrorOrg) {
CString token;
CString username, password, orgname;
m_orgname.GetWindowTextW(orgname);
m_username.GetWindowTextW(username);
m_password.GetWindowTextW(password);
// trim spaces
orgname = CString(std::regex_replace(LauncherUtils::cStringToStd(orgname), std::regex("^ +| +$|( ) +"), "$1").c_str());
username = CString(std::regex_replace(LauncherUtils::cStringToStd(username), std::regex("^ +| +$|( ) +"), "$1").c_str());
// encode
username = LauncherUtils::urlEncodeString(username);
password = LauncherUtils::urlEncodeString(password);
LauncherUtils::ResponseError error;
if (orgname.GetLength() > 0 && username.GetLength() > 0 && password.GetLength() > 0) {
theApp._manager.addToLog(_T("Trying to get organization data"));
if (getHQInfo(orgname)) {
theApp._manager.addToLog(_T("Organization data received."));
theApp._manager.addToLog(_T("Trying to log in with credentials"));
error = theApp._manager.getAccessTokenForCredentials(username, password);
if (error == LauncherUtils::ResponseError::NoError) {
theApp._manager.addToLog(_T("Logged in correctly."));
setDrawDialog(DrawStep::DrawChoose);
} else if (error == LauncherUtils::ResponseError::BadCredentials) {
theApp._manager.addToLog(_T("Bad credentials. Try again"));
setDrawDialog(DrawStep::DrawLoginErrorCred);
} else {
theApp._manager.addToLog(_T("Error Reading or retrieving response."));
MessageBox(L"Error Reading or retrieving response.", L"Network Error", MB_OK | MB_ICONERROR);
}
} else {
theApp._manager.addToLog(_T("Organization name does not exist."));
setDrawDialog(DrawStep::DrawLoginErrorOrg);
}
}
}
}
void CLauncherDlg::drawBackground(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamBackground(pRenderTarget, IDB_PNG1, _T("PNG"));
auto size = GetRenderTarget()->GetSize();
CD2DRectF backRec(0.0f, 0.0f, size.width, size.height);
GetRenderTarget()->DrawBitmap(&m_pBitmamBackground, backRec);
pRenderTarget->DrawBitmap(&m_pBitmamBackground, backRec);
}
void CLauncherDlg::drawLogo(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG2, _T("PNG"));
auto size = pRenderTarget->GetSize();
int logoWidth = 231;
int logoHeight = 173;
float logoPosX = 0.5f * (size.width - logoWidth);
float logoPosY = 0.5f * (size.height - logoHeight);
CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight);
pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec);
}
void CLauncherDlg::drawSmallLogo(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamLogo(pRenderTarget, IDB_PNG5, _T("PNG"));
auto size = pRenderTarget->GetSize();
int xPadding = 6;
int yPadding = 22;
int logoWidth = 100;
int logoHeight = 18;
float logoPosX = size.width - logoWidth - xPadding;
float logoPosY = size.height - logoHeight - yPadding;
CD2DRectF logoRec(logoPosX, logoPosY, logoPosX + logoWidth, logoPosY + logoHeight);
pRenderTarget->DrawBitmap(&m_pBitmamLogo, logoRec);
}
void CLauncherDlg::drawVoxel(CHwndRenderTarget* pRenderTarget) {
CD2DBitmap m_pBitmamVoxel(pRenderTarget, IDB_PNG4, _T("PNG"));
auto size = pRenderTarget->GetSize();
int logoWidth = 132;
int logoHeight = 134;
float voxelPosX = 0.5f * (size.width - logoWidth);
float voxelPosY = 0.5f * (size.height - logoHeight);
CD2DRectF voxelRec(voxelPosX, voxelPosY, voxelPosX + logoWidth, voxelPosY + logoHeight);
auto midPoint = D2D1::Point2F(0.5f * size.width, 0.5f * size.height);
_logoRotation += 2.0f;
CD2DSolidColorBrush brush(pRenderTarget, D2D1::ColorF(0.0f, 0.0f, 0.0f));
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation - 2.0f, midPoint));
pRenderTarget->FillRectangle(voxelRec, &brush);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(_logoRotation, midPoint));
pRenderTarget->DrawBitmap(&m_pBitmamVoxel, voxelRec);
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
}
void CLauncherDlg::drawProgress(CHwndRenderTarget* pRenderTarget, float progress, const D2D1::ColorF& color) {
auto size = pRenderTarget->GetSize();
if (progress == 0.0f) {
return;
} else {
progress = min(1.0f, progress);
}
CRect winRec;
float fullHeight = (float)size.height;
float halfHeight = 0.5f * (float)size.height;
CD2DRectF bkCircleRect1 = CD2DRectF(0.0f, 0.0f, fullHeight, fullHeight);
float progPos = halfHeight + (float)(size.width - size.height) * progress;
CD2DRectF bkCircleRect2 = CD2DRectF(progPos - halfHeight, 0.0f, progPos + halfHeight, fullHeight);
CD2DRectF bkRect = CD2DRectF(halfHeight, 0.0f, progPos, fullHeight);
CD2DEllipse bkCircle1 = CD2DEllipse(bkCircleRect1);
CD2DEllipse bkCircle2 = CD2DEllipse(bkCircleRect2);
CD2DSolidColorBrush brush(pRenderTarget, color);
pRenderTarget->FillEllipse(bkCircle1, &brush);
pRenderTarget->FillEllipse(bkCircle2, &brush);
pRenderTarget->FillRectangle(bkRect, &brush);
}
void CLauncherDlg::showWindows(std::vector<CStatic*> windows, bool show) {
for (auto window : windows) {
window->ShowWindow(show ? SW_SHOW : SW_HIDE);
}
}
void CLauncherDlg::prepareLogin(DrawStep step) {
m_voxel->ShowWindow(SW_HIDE);
m_progress->ShowWindow(SW_HIDE);
m_orgname_banner->SetWindowTextW(_T("Organization Name"));
m_username_banner->SetWindowTextW(_T("Username"));
m_password_banner->SetWindowTextW(_T("Password"));
CString editText;
m_orgname.GetWindowTextW(editText);
m_orgname_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_username.GetWindowTextW(editText);
m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_password.GetWindowTextW(editText);
m_password_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_orgname.ShowWindow(SW_SHOW);
m_username.ShowWindow(SW_SHOW);
m_password.ShowWindow(SW_SHOW);
CString actionText = step == DrawStep::DrawLoginLogin ? _T("Please log in") : _T("Uh-oh, we have a problem");
CString messageText = step == DrawStep::DrawLoginLogin ? _T("Be sure you've uploaded your Avatar before signing in.") :
step == DrawStep::DrawLoginErrorCred ? _T("There is a problem with your credentials.\n Please try again.") : _T("There is a problem with your Organization name.\n Please try again.");
m_action_label->SetWindowTextW(actionText);
m_message_label->SetWindowTextW(messageText);
m_action_label->ShowWindow(SW_SHOW);
m_message_label->ShowWindow(SW_SHOW);
m_btnNext.ShowWindow(SW_SHOW);
m_trouble->SetWindowTextW(_T("Having Trouble?"));
m_trouble->ShowWindow(SW_SHOW);
m_trouble_link.ShowWindow(SW_SHOW);
}
void CLauncherDlg::prepareChoose() {
m_orgname.ShowWindow(SW_HIDE);
m_username.SetWindowTextW(_T(""));
m_username_banner->SetWindowTextW(_T("Display Name"));
CString editText;
m_username.GetWindowTextW(editText);
m_username_banner->ShowWindow(editText.GetLength() == 0 ? SW_SHOW : SW_HIDE);
m_password.ShowWindow(SW_HIDE);
m_orgname_banner->ShowWindow(SW_HIDE);
m_password_banner->ShowWindow(SW_HIDE);
m_action_label->SetWindowTextW(_T("Choose a display name"));
m_message_label->SetWindowTextW(_T("This is the name that your teammates will see."));
m_terms->ShowWindow(SW_SHOW);
m_terms_link.ShowWindow(SW_SHOW);
m_terms->SetWindowTextW(_T("By signing in, you agree to the High Fidelity"));
m_terms_link.SetWindowTextW(_T("Terms of Service"));
setVerticalElement(&m_btnNext, -35, 0, false);
m_btnNext.ShowWindow(SW_SHOW);
}
void CLauncherDlg::prepareProcess(DrawStep step) {
m_trouble->ShowWindow(SW_HIDE);
m_trouble_link.ShowWindow(SW_HIDE);
m_terms->ShowWindow(SW_HIDE);
m_terms_link.ShowWindow(SW_HIDE);
m_orgname_banner->ShowWindow(SW_HIDE);
m_username_banner->ShowWindow(SW_HIDE);
m_password_banner->ShowWindow(SW_HIDE);
m_orgname.ShowWindow(SW_HIDE);
m_username.ShowWindow(SW_HIDE);
m_password.ShowWindow(SW_HIDE);
m_action_label->SetWindowTextW(_T(""));
m_message_label->SetWindowTextW(_T(""));
m_btnNext.ShowWindow(SW_HIDE);
m_action_label->ShowWindow(SW_HIDE);
m_message_label->ShowWindow(SW_HIDE);
m_voxel->ShowWindow(SW_SHOW);
m_progress->ShowWindow(SW_SHOW);
CString actionText = _T("");
CString messageText = _T("");
switch (step) {
case DrawStep::DrawProcessSetup:
actionText = _T("We're building your virtual HQ");
messageText = _T("Set up may take several minutes.");
break;
case DrawStep::DrawProcessUpdate:
actionText = _T("Getting updates...");
messageText = _T("We're getting the latest and greatest for you, one sec.");
break;
case DrawStep::DrawProcessFinishHq:
actionText = _T("Your new HQ is all setup");
messageText = _T("Thanks for being patient.");
break;
case DrawStep::DrawProcessFinishUpdate:
actionText = _T("You're good to go!");
messageText = _T("Thanks for being patient.");
break;
case DrawStep::DrawProcessUninstall:
actionText = _T("Uninstalling...");
messageText = _T("It'll take one sec.");
break;
case DrawStep::DrawError:
actionText = _T("Uh oh.");
messageText = _T("We seem to have a problem.\nPlease restart HQ.");
setVerticalElement(m_message2_label, 0, 5, false);
setVerticalElement(&m_btnNext, 10);
m_btnNext.ShowWindow(SW_SHOW);
m_progress->ShowWindow(SW_HIDE);
break;
default:
break;
}
m_action2_label->SetWindowTextW(actionText);
m_message2_label->SetWindowTextW(messageText);
m_action2_label->ShowWindow(SW_SHOW);
m_message2_label->ShowWindow(SW_SHOW);
}
BOOL CLauncherDlg::getTextFormat(int resID, TextFormat& formatOut) {
// Set default values for message
BOOL isText = TRUE;
formatOut.color = COLOR_LIGHT_GREY;
formatOut.isBold = false;
formatOut.isButton = false;
formatOut.size = MESSAGE_FONT_SIZE;
formatOut.underlined = false;
switch (resID) {
case IDC_VOXEL:
case IDD_LAUNCHER_DIALOG:
isText = FALSE;
case IDC_MESSAGE_LABEL:
case IDC_MESSAGE2_LABEL:
// Default values
break;
case IDC_ACTION_LABEL:
case IDC_ACTION2_LABEL:
formatOut.size = ACTION_FONT_SIZE;
formatOut.isBold = true;
formatOut.color = COLOR_LIGHTER_GREY;
break;
case IDC_USERNAME:
case IDC_PASSWORD:
case IDC_ORGNAME:
formatOut.color = COLOR_WHITE;
formatOut.size = FIELDS_FONT_SIZE;
formatOut.underlined = true;
break;
case IDC_USERNAME_BANNER:
case IDC_PASSWORD_BANNER:
case IDC_ORGNAME_BANNER:
formatOut.size = FIELDS_FONT_SIZE;
formatOut.color = COLOR_GREY;
break;
case IDC_VERSION:
case IDC_TERMS:
formatOut.size = TERMS_FONT_SIZE;
break;
case IDC_TERMS_LINK:
formatOut.size = TERMS_FONT_SIZE;
formatOut.isBold = true;
break;
case IDC_TROUBLE:
formatOut.size = TROUBLE_FONT_SIZE;
formatOut.color = COLOR_BLUE;
break;
}
return isText;
}
HBRUSH CLauncherDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
TextFormat textFormat;
int resId = pWnd->GetDlgCtrlID();
if (getTextFormat(resId, textFormat)) {
pDC->SetTextColor(textFormat.color);
pDC->SetBkMode(TRANSPARENT);
CFont textFont;
CString fontFamily = textFormat.isBold ? FIRASANS_SEMIBOLD : FIRASANS_REGULAR;
if (LauncherUtils::getFont(fontFamily, textFormat.size, textFormat.isBold, textFont)) {
pDC->SelectObject(&textFont);
}
if (textFormat.underlined) {
CRect rect;
pWnd->GetClientRect(&rect);
int borderThick = 1;
int padding = 4;
CRect lineRect = CRect(rect.left + padding, rect.bottom, rect.right - padding, rect.bottom + borderThick);
lineRect.MoveToY(lineRect.bottom + 1);
pDC->FillSolidRect(lineRect, COLOR_GREY);
}
}
return (HBRUSH)GetStockObject(BLACK_BRUSH);
}
void CLauncherDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
CRect rect = lpDrawItemStruct->rcItem;
CRect defrect = rect;
CString btnName = _T("");
int xpan = 0;
if (nIDCtl == IDC_BUTTON_NEXT) {
if (_drawStep == DrawStep::DrawChoose || _drawStep == DrawStep::DrawLoginLogin) {
btnName += _drawStep == DrawStep::DrawLoginLogin ? _T("LOG IN") : _T("NEXT");
int xpan = -20;
defrect = CRect(rect.left - xpan, rect.top, rect.right + xpan, rect.bottom);
} else if (_drawStep == DrawStep::DrawError) {
btnName += _T("RESTART");
} else {
btnName += _T("TRY AGAIN");
}
int borderThick = 2;
dc.FillSolidRect(rect, COLOR_BLACK);
dc.FillSolidRect(defrect, COLOR_WHITE);
defrect.DeflateRect(borderThick, borderThick, borderThick, borderThick);
dc.FillSolidRect(defrect, COLOR_BLACK);
UINT state = lpDrawItemStruct->itemState;
dc.SetTextColor(COLOR_WHITE);
CFont buttonFont;
if (LauncherUtils::getFont(FIRASANS_SEMIBOLD, BUTTON_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(btnName, CRect(rect.left, rect.top + 4, rect.right, rect.bottom - 8), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
dc.Detach();
} else if (nIDCtl == IDC_TROUBLE_LINK) {
dc.FillSolidRect(rect, COLOR_BLACK);
dc.SetTextColor(COLOR_BLUE);
CFont buttonFont;
if (LauncherUtils::getFont(FIRASANS_SEMIBOLD, TROUBLE_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(_T("Having Trouble"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_CENTER | DT_VCENTER | DT_SINGLELINE);
} else if (nIDCtl == IDC_TERMS_LINK) {
dc.FillSolidRect(rect, COLOR_BLACK);
dc.SetTextColor(COLOR_LIGHT_GREY);
CFont buttonFont;
if (LauncherUtils::getFont(FIRASANS_SEMIBOLD, TERMS_FONT_SIZE, true, buttonFont)) {
dc.SelectObject(buttonFont);
}
dc.DrawText(_T("Terms of Service"), CRect(rect.left, rect.top, rect.right, rect.bottom), DT_LEFT | DT_TOP | DT_SINGLELINE);
}
}
void CLauncherDlg::redrawBanner(const CEdit& edit, CStatic* banner) {
CString editText;
edit.GetWindowTextW(editText);
if (editText.GetLength() == 0) {
banner->Invalidate();
}
}
void CLauncherDlg::OnOrgEditChangeFocus() {
redrawBanner(m_username, m_username_banner);
redrawBanner(m_password, m_password_banner);
}
void CLauncherDlg::OnUserEditChangeFocus() {
redrawBanner(m_orgname, m_orgname_banner);
redrawBanner(m_password, m_password_banner);
}
void CLauncherDlg::OnPassEditChangeFocus() {
redrawBanner(m_orgname, m_orgname_banner);
redrawBanner(m_username, m_username_banner);
}
BOOL CLauncherDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (pWnd->IsKindOf(RUNTIME_CLASS(CButton))) {
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_HAND));
return TRUE;
}
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
void CLauncherDlg::OnTimer(UINT_PTR nIDEvent) {
if (theApp._manager.hasFailed() && _drawStep != DrawStep::DrawError) {
theApp._manager.saveErrorLog();
prepareProcess(DrawStep::DrawError);
setDrawDialog(DrawStep::DrawError, false);
}
if (_drawStep != DrawStep::DrawError) {
if (_drawStep == DrawStep::DrawProcessSetup ||
_drawStep == DrawStep::DrawProcessUpdate ||
_drawStep == DrawStep::DrawProcessUninstall ||
_drawStep == DrawStep::DrawProcessFinishHq ||
_drawStep == DrawStep::DrawProcessFinishUpdate) {
// Refresh
setDrawDialog(_drawStep, true);
}
if (theApp._manager.needsSelfUpdate()) {
if (theApp._manager.needsSelfDownload()) {
theApp._manager.downloadNewLauncher();
} else {
if (_splashStep > SPLASH_DURATION && _splashStep < 2 * SPLASH_DURATION) {
float progress = (float)(_splashStep - SPLASH_DURATION) / SPLASH_DURATION;
if (theApp._manager.willContinueUpdating()) {
progress = CONTINUE_UPDATING_GLOBAL_OFFSET * progress;
progress = min(progress, CONTINUE_UPDATING_GLOBAL_OFFSET);
}
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, progress);
_splashStep++;
}
if (theApp._manager.needsRestartNewLauncher()) {
if (_splashStep >= 2 * SPLASH_DURATION) {
theApp._manager.restartNewLauncher();
exit(0);
}
}
}
}
LauncherManager::ContinueActionOnStart continueAction = theApp._manager.getContinueAction();
if (_showSplash) {
if (_splashStep == 0) {
if (theApp._manager.needsUninstall()) {
theApp._manager.addToLog(_T("Waiting to uninstall"));
setDrawDialog(DrawStep::DrawProcessUninstall);
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueUpdate) {
setDrawDialog(DrawStep::DrawProcessUpdate);
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 0.0f);
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueLogIn) {
_splashStep = SPLASH_DURATION;
} else if (continueAction == LauncherManager::ContinueActionOnStart::ContinueFinish) {
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, 1.0f);
setDrawDialog(DrawStep::DrawProcessFinishUpdate);
_splashStep = SPLASH_DURATION;
_showSplash = false;
} else {
theApp._manager.addToLog(_T("Start splash screen"));
setDrawDialog(DrawStep::DrawLogo);
}
} else if (_splashStep > SPLASH_DURATION && !theApp._manager.needsToWait()) {
_showSplash = false;
if (theApp._manager.shouldShutDown()) {
if (_applicationWND != NULL) {
::SetForegroundWindow(_applicationWND);
::SetActiveWindow(_applicationWND);
}
if (LauncherUtils::isProcessWindowOpened(L"interface.exe")) {
exit(0);
}
} else if (theApp._manager.needsUpdate()) {
startProcess();
} else if (theApp._manager.needsUninstall()) {
if (theApp._manager.uninstallApplication()) {
theApp._manager.addToLog(_T("HQ uninstalled successfully."));
exit(0);
} else {
theApp._manager.addToLog(_T("HQ failed to uninstall."));
theApp._manager.setFailed(true);
}
} else if (theApp._manager.needsSelfUpdate()) {
setDrawDialog(DrawStep::DrawProcessUpdate);
} else {
theApp._manager.addToLog(_T("Starting login"));
setDrawDialog(DrawStep::DrawLoginLogin);
}
} else if (theApp._manager.needsUninstall()) {
theApp._manager.updateProgress(LauncherManager::ProcessType::Uninstall, (float)_splashStep / SPLASH_DURATION);
}
_splashStep++;
} else if (theApp._manager.shouldShutDown()) {
if (LauncherUtils::isProcessWindowOpened(L"interface.exe")) {
exit(0);
}
}
if (theApp._manager.shouldLaunch()) {
if (theApp._manager.needsInstall() || theApp._manager.needsUpdate()) {
auto finishProcess = theApp._manager.needsUpdate() ? DrawStep::DrawProcessFinishUpdate : DrawStep::DrawProcessFinishHq;
setDrawDialog(finishProcess);
}
_applicationWND = theApp._manager.launchApplication();
}
}
if (theApp._manager.needsToSelfInstall()) {
theApp._manager.tryToInstallLauncher(TRUE);
}
}
void CLauncherDlg::setVerticalElement(CWnd* element, int verticalOffset, int heightOffset, bool fromMainWindowBottom) {
CRect elementRec;
CRect windowRec;
if (element != NULL) {
element->GetWindowRect(&elementRec);
ScreenToClient(&elementRec);
int offset = verticalOffset;
if (fromMainWindowBottom) {
GetWindowRect(&windowRec);
ScreenToClient(&windowRec);
int currentDistance = windowRec.bottom - elementRec.bottom;
offset = currentDistance - verticalOffset;
}
elementRec.bottom = elementRec.bottom + offset + heightOffset;
elementRec.top = elementRec.top + offset;
element->MoveWindow(elementRec, FALSE);
}
}
void CLauncherDlg::setDrawDialog(DrawStep step, BOOL isUpdate) {
_drawStep = step;
float progress = 0.0f;
auto m_pRenderTarget = GetRenderTarget();
auto m_voxelRenderTarget = m_voxel->GetRenderTarget();
auto m_progressRenderTarget = m_progress->GetRenderTarget();
switch (_drawStep) {
case DrawStep::DrawLogo: {
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
CRect redrawRec;
GetClientRect(redrawRec);
redrawRec.top = redrawRec.bottom - 30;
RedrawWindow(redrawRec);
break;
}
case DrawStep::DrawLoginLogin:
case DrawStep::DrawLoginErrorOrg:
case DrawStep::DrawLoginErrorCred:
prepareLogin(_drawStep);
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
break;
case DrawStep::DrawChoose:
prepareChoose();
m_pRenderTarget->BeginDraw();
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
break;
case DrawStep::DrawError:
case DrawStep::DrawProcessFinishHq:
case DrawStep::DrawProcessFinishUpdate:
case DrawStep::DrawProcessUpdate:
case DrawStep::DrawProcessUninstall:
case DrawStep::DrawProcessSetup:
if (!isUpdate) {
m_voxelRenderTarget->BeginDraw();
m_voxelRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 1.0f));
m_voxelRenderTarget->EndDraw();
m_pRenderTarget->BeginDraw();
prepareProcess(_drawStep);
drawBackground(m_pRenderTarget);
drawSmallLogo(m_pRenderTarget);
m_pRenderTarget->EndDraw();
RedrawWindow();
}
m_progressRenderTarget->BeginDraw();
m_progressRenderTarget->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 1.0f));
drawProgress(m_progressRenderTarget, 1.0f, D2D1::ColorF(0.2f, 0.2f, 0.2f));
drawProgress(m_progressRenderTarget, theApp._manager.getProgress(), D2D1::ColorF(0.0f, 0.62f, 0.9f));
m_progressRenderTarget->EndDraw();
m_voxelRenderTarget->BeginDraw();
drawVoxel(m_voxelRenderTarget);
m_voxelRenderTarget->EndDraw();
break;
default:
break;
}
}