Files
Justin Marshall 8c4a087aa9 Filesystem update.
2026-05-09 07:54:14 -07:00

652 lines
15 KiB
C++

/*
===========================================================================
IceTech GPL Source Code
Copyright (C) 2026 Justin Marshall
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
IceTech Source Code 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.
IceTech Source Code 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 IceTech Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "qe3.h"
#include "WaitDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define WAITDLG_TIMER_ANIMATE 1001
#define WAITDLG_TIMER_MS 33
// Runtime size. This makes the old tiny resource dialog larger without
// needing to edit the .rc file.
#define WAITDLG_WIDTH 560
#define WAITDLG_HEIGHT 260
/////////////////////////////////////////////////////////////////////////////
// CWaitDlg dialog
CWaitDlg::CWaitDlg(CWnd* pParent, const char* msg)
: CDialog(CWaitDlg::IDD, pParent) {
waitStr = msg ? msg : "Wait...";
text = waitStr;
titleStr = "IceTech Radiant";
cancelPressed = false;
cancelAllowed = false;
progressPercent = -1;
marqueeOffset = 0;
pulseFrame = 0;
colorBackTop = RGB(13, 17, 26);
colorBackBottom = RGB(24, 30, 45);
colorPanel = RGB(28, 35, 52);
colorPanelBorder = RGB(62, 78, 112);
colorText = RGB(236, 242, 255);
colorMutedText = RGB(148, 162, 190);
colorAccent = RGB(83, 169, 255);
colorAccent2 = RGB(127, 96, 255);
colorProgressBack = RGB(15, 20, 31);
colorDanger = RGB(255, 92, 112);
InitModernResources();
Create(CWaitDlg::IDD, pParent);
}
CWaitDlg::~CWaitDlg() {
KillTimer(WAITDLG_TIMER_ANIMATE);
FreeModernResources();
if (g_pParentWnd) {
g_pParentWnd->SetBusy(false);
}
}
void CWaitDlg::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX);
// Keep the original DDX mapping alive for old resource compatibility.
DDX_Text(pDX, IDC_WAITSTR, waitStr);
}
BEGIN_MESSAGE_MAP(CWaitDlg, CDialog)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
ON_WM_TIMER()
ON_WM_SIZE()
ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// Modern resources
void CWaitDlg::InitModernResources(void) {
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = -23;
lf.lfWeight = FW_SEMIBOLD;
lf.lfQuality = CLEARTYPE_QUALITY;
strcpy(lf.lfFaceName, "Segoe UI");
titleFont.CreateFontIndirect(&lf);
memset(&lf, 0, sizeof(lf));
lf.lfHeight = -15;
lf.lfWeight = FW_NORMAL;
lf.lfQuality = CLEARTYPE_QUALITY;
strcpy(lf.lfFaceName, "Segoe UI");
messageFont.CreateFontIndirect(&lf);
memset(&lf, 0, sizeof(lf));
lf.lfHeight = -12;
lf.lfWeight = FW_NORMAL;
lf.lfQuality = CLEARTYPE_QUALITY;
strcpy(lf.lfFaceName, "Segoe UI");
smallFont.CreateFontIndirect(&lf);
transparentBrush.CreateStockObject(NULL_BRUSH);
}
void CWaitDlg::FreeModernResources(void) {
if (titleFont.GetSafeHandle()) {
titleFont.DeleteObject();
}
if (messageFont.GetSafeHandle()) {
messageFont.DeleteObject();
}
if (smallFont.GetSafeHandle()) {
smallFont.DeleteObject();
}
if (transparentBrush.GetSafeHandle()) {
transparentBrush.DeleteObject();
}
}
/////////////////////////////////////////////////////////////////////////////
// Dialog init
BOOL CWaitDlg::OnInitDialog() {
CDialog::OnInitDialog();
ModifyStyleEx(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE, 0);
ModifyStyle(WS_THICKFRAME, 0);
SetWindowText("Please wait");
ResizeAndCenterDialog();
CWnd* pWaitText = GetDlgItem(IDC_WAITSTR);
if (pWaitText && pWaitText->GetSafeHwnd()) {
pWaitText->ShowWindow(SW_HIDE);
}
CWnd* pCancelButton = GetDlgItem(IDCANCEL);
if (pCancelButton && pCancelButton->GetSafeHwnd()) {
pCancelButton->SetFont(&messageFont);
pCancelButton->SetWindowText("Cancel");
}
UpdateData(FALSE);
AllowCancel(false);
LayoutModernControls();
SetTimer(WAITDLG_TIMER_ANIMATE, WAITDLG_TIMER_MS, NULL);
ShowWindow(SW_SHOW);
Invalidate(FALSE);
UpdateWindow();
return TRUE;
}
void CWaitDlg::ResizeAndCenterDialog(void) {
CRect rcWindow;
GetWindowRect(&rcWindow);
int width = WAITDLG_WIDTH;
int height = WAITDLG_HEIGHT;
CWnd* pParent = GetParent();
CRect rcParent;
if (pParent && pParent->GetSafeHwnd()) {
pParent->GetWindowRect(&rcParent);
}
else {
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcParent, 0);
}
int x = rcParent.left + ((rcParent.Width() - width) / 2);
int y = rcParent.top + ((rcParent.Height() - height) / 2);
SetWindowPos(NULL, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
}
/////////////////////////////////////////////////////////////////////////////
// Public API
void CWaitDlg::SetText(const char* msg, bool append) {
if (append) {
waitStr = text.c_str();
waitStr += "\r\n";
waitStr += msg ? msg : "";
text = waitStr;
}
else {
waitStr = msg ? msg : "";
text = waitStr;
}
UpdateData(FALSE);
Invalidate(FALSE);
UpdateWindow();
ShowWindow(SW_SHOWNORMAL);
}
void CWaitDlg::SetTitleText(const char* title) {
titleStr = title ? title : "";
Invalidate(FALSE);
UpdateWindow();
}
void CWaitDlg::SetProgress(int percent) {
if (percent < 0) {
progressPercent = -1;
}
else {
if (percent > 100) {
percent = 100;
}
progressPercent = percent;
}
Invalidate(FALSE);
UpdateWindow();
}
void CWaitDlg::StepProgress(int amount) {
if (progressPercent < 0) {
progressPercent = 0;
}
progressPercent += amount;
if (progressPercent < 0) {
progressPercent = 0;
}
if (progressPercent > 100) {
progressPercent = 100;
}
Invalidate(FALSE);
UpdateWindow();
}
int CWaitDlg::GetProgress(void) const {
return progressPercent;
}
void CWaitDlg::AllowCancel(bool enable) {
cancelAllowed = enable;
CWnd* pCancelButton = GetDlgItem(IDCANCEL);
ASSERT(pCancelButton);
if (pCancelButton && pCancelButton->GetSafeHwnd()) {
pCancelButton->ShowWindow(enable ? SW_SHOW : SW_HIDE);
}
LayoutModernControls();
Invalidate(FALSE);
}
bool CWaitDlg::CancelPressed(void) {
PumpMessages();
return cancelPressed;
}
void CWaitDlg::PumpMessages(void) {
#if _MSC_VER >= 1300
MSG* msg = AfxGetCurrentMessage();
#else
MSG* msg = &m_msgCur;
#endif
while (::PeekMessage(msg, NULL, 0, 0, PM_NOREMOVE)) {
if (!AfxGetApp()->PumpMessage()) {
break;
}
}
}
void CWaitDlg::OnCancel() {
cancelPressed = true;
}
/////////////////////////////////////////////////////////////////////////////
// Layout
void CWaitDlg::OnSize(UINT nType, int cx, int cy) {
CDialog::OnSize(nType, cx, cy);
LayoutModernControls();
Invalidate(FALSE);
}
void CWaitDlg::LayoutModernControls(void) {
if (!GetSafeHwnd()) {
return;
}
CRect rcClient;
GetClientRect(&rcClient);
CWnd* pCancelButton = GetDlgItem(IDCANCEL);
if (pCancelButton && pCancelButton->GetSafeHwnd()) {
const int buttonW = 112;
const int buttonH = 32;
const int margin = 26;
CRect rcButton;
rcButton.left = rcClient.right - margin - buttonW;
rcButton.top = rcClient.bottom - margin - buttonH;
rcButton.right = rcButton.left + buttonW;
rcButton.bottom = rcButton.top + buttonH;
pCancelButton->MoveWindow(rcButton);
}
}
/////////////////////////////////////////////////////////////////////////////
// Paint
BOOL CWaitDlg::OnEraseBkgnd(CDC* pDC) {
return TRUE;
}
HBRUSH CWaitDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) {
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(colorText);
return (HBRUSH)transparentBrush.GetSafeHandle();
}
void CWaitDlg::OnTimer(UINT_PTR nIDEvent) {
if (nIDEvent == WAITDLG_TIMER_ANIMATE) {
pulseFrame++;
marqueeOffset += 5;
if (marqueeOffset > 10000) {
marqueeOffset = 0;
}
Invalidate(FALSE);
return;
}
CDialog::OnTimer(nIDEvent);
}
void CWaitDlg::OnPaint() {
CPaintDC dcPaint(this);
CRect rcClient;
GetClientRect(&rcClient);
CDC memDC;
CBitmap memBmp;
CBitmap* oldBmp = NULL;
memDC.CreateCompatibleDC(&dcPaint);
memBmp.CreateCompatibleBitmap(&dcPaint, rcClient.Width(), rcClient.Height());
oldBmp = memDC.SelectObject(&memBmp);
DrawModernBackground(memDC, rcClient);
DrawHeader(memDC, rcClient);
DrawMessage(memDC, rcClient);
DrawProgress(memDC, rcClient);
dcPaint.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &memDC, 0, 0, SRCCOPY);
if (oldBmp) {
memDC.SelectObject(oldBmp);
}
}
void CWaitDlg::DrawModernBackground(CDC& dc, const CRect& rcClient) {
TRIVERTEX vertex[2];
vertex[0].x = rcClient.left;
vertex[0].y = rcClient.top;
vertex[0].Red = GetRValue(colorBackTop) << 8;
vertex[0].Green = GetGValue(colorBackTop) << 8;
vertex[0].Blue = GetBValue(colorBackTop) << 8;
vertex[0].Alpha = 0x0000;
vertex[1].x = rcClient.right;
vertex[1].y = rcClient.bottom;
vertex[1].Red = GetRValue(colorBackBottom) << 8;
vertex[1].Green = GetGValue(colorBackBottom) << 8;
vertex[1].Blue = GetBValue(colorBackBottom) << 8;
vertex[1].Alpha = 0x0000;
GRADIENT_RECT gRect;
gRect.UpperLeft = 0;
gRect.LowerRight = 1;
dc.GradientFill(vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_V);
CRect panel = rcClient;
panel.DeflateRect(14, 14, 14, 14);
DrawRoundedRect(dc, panel, colorPanel, colorPanelBorder, 18);
// Top glass sheen.
CRect sheen = panel;
sheen.bottom = sheen.top + 70;
TRIVERTEX sheenVertex[2];
sheenVertex[0].x = sheen.left;
sheenVertex[0].y = sheen.top;
sheenVertex[0].Red = 60 << 8;
sheenVertex[0].Green = 78 << 8;
sheenVertex[0].Blue = 118 << 8;
sheenVertex[0].Alpha = 0x0000;
sheenVertex[1].x = sheen.right;
sheenVertex[1].y = sheen.bottom;
sheenVertex[1].Red = GetRValue(colorPanel) << 8;
sheenVertex[1].Green = GetGValue(colorPanel) << 8;
sheenVertex[1].Blue = GetBValue(colorPanel) << 8;
sheenVertex[1].Alpha = 0x0000;
dc.GradientFill(sheenVertex, 2, &gRect, 1, GRADIENT_FILL_RECT_V);
// Header separator.
CRect accent = panel;
accent.left += 24;
accent.right -= 24;
accent.top += 66;
accent.bottom = accent.top + 1;
CPen accentPen(PS_SOLID, 1, RGB(72, 91, 130));
CPen* oldPen = dc.SelectObject(&accentPen);
dc.MoveTo(accent.left, accent.top);
dc.LineTo(accent.right, accent.top);
dc.SelectObject(oldPen);
}
void CWaitDlg::DrawHeader(CDC& dc, const CRect& rcClient) {
CRect panel = rcClient;
panel.DeflateRect(14, 14, 14, 14);
CRect iconRc;
iconRc.left = panel.left + 26;
iconRc.top = panel.top + 20;
iconRc.right = iconRc.left + 30;
iconRc.bottom = iconRc.top + 30;
int glow = 20 + (pulseFrame % 50);
if (glow > 45) {
glow = 70 - glow;
}
COLORREF coreColor = RGB(
min(255, GetRValue(colorAccent) + glow),
min(255, GetGValue(colorAccent) + glow / 2),
min(255, GetBValue(colorAccent))
);
DrawRoundedRect(dc, iconRc, coreColor, RGB(155, 205, 255), 8);
CRect inner = iconRc;
inner.DeflateRect(7, 7, 7, 7);
DrawRoundedRect(dc, inner, RGB(18, 25, 38), RGB(150, 220, 255), 4);
CFont* oldFont = dc.SelectObject(&titleFont);
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(colorText);
CRect titleRc;
titleRc.left = iconRc.right + 14;
titleRc.top = panel.top + 17;
titleRc.right = panel.right - 26;
titleRc.bottom = titleRc.top + 30;
dc.DrawText(titleStr, &titleRc, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
dc.SelectObject(&smallFont);
dc.SetTextColor(colorMutedText);
CRect subRc = titleRc;
subRc.top += 28;
subRc.bottom = subRc.top + 20;
CString subText = "Working on the current Radiant task";
dc.DrawText(subText, &subRc, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
dc.SelectObject(oldFont);
}
void CWaitDlg::DrawMessage(CDC& dc, const CRect& rcClient) {
CRect panel = rcClient;
panel.DeflateRect(14, 14, 14, 14);
// Move status text higher and reserve a larger footer.
const int headerBottom = panel.top + 70;
const int footerHeight = cancelAllowed ? 138 : 108;
CRect textRc;
textRc.left = panel.left + 28;
textRc.top = headerBottom + 6;
textRc.right = panel.right - 28;
textRc.bottom = panel.bottom - footerHeight;
if (textRc.bottom < textRc.top + 26) {
textRc.bottom = textRc.top + 26;
}
CFont* oldFont = dc.SelectObject(&messageFont);
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(colorText);
CString drawText = waitStr;
if (drawText.IsEmpty()) {
drawText = "Please wait...";
}
dc.DrawText(drawText, &textRc, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_END_ELLIPSIS);
dc.SelectObject(oldFont);
}
void CWaitDlg::DrawProgress(CDC& dc, const CRect& rcClient) {
CRect panel = rcClient;
panel.DeflateRect(14, 14, 14, 14);
// Push progress lower so it never overlaps the status text.
CRect barRc;
barRc.left = panel.left + 28;
barRc.right = panel.right - 28;
if (cancelAllowed) {
barRc.bottom = panel.bottom - 54;
}
else {
barRc.bottom = panel.bottom - 28;
}
barRc.top = barRc.bottom - 18;
DrawRoundedRect(dc, barRc, colorProgressBack, RGB(54, 68, 96), 9);
CRect fillRc = barRc;
fillRc.DeflateRect(2, 2, 2, 2);
if (progressPercent >= 0) {
int fillW = MulDiv(fillRc.Width(), progressPercent, 100);
CRect filled = fillRc;
filled.right = filled.left + fillW;
if (filled.Width() > 0) {
DrawRoundedRect(dc, filled, colorAccent, RGB(125, 205, 255), 8);
}
CString percentText;
percentText.Format("%d%%", progressPercent);
CFont* oldFont = dc.SelectObject(&smallFont);
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(colorMutedText);
CRect labelRc = barRc;
labelRc.top = barRc.bottom + 6;
labelRc.bottom = labelRc.top + 18;
dc.DrawText(percentText, &labelRc, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
dc.SelectObject(oldFont);
}
else {
const int chunkW = max(64, fillRc.Width() / 4);
const int travelW = fillRc.Width() + chunkW;
const int x = fillRc.left - chunkW + (marqueeOffset % travelW);
CRect chunk;
chunk.left = x;
chunk.right = x + chunkW;
chunk.top = fillRc.top;
chunk.bottom = fillRc.bottom;
CRect clipped;
clipped.IntersectRect(&chunk, &fillRc);
if (!clipped.IsRectEmpty()) {
DrawRoundedRect(dc, clipped, colorAccent, RGB(125, 205, 255), 8);
}
CRect glowChunk = chunk;
glowChunk.OffsetRect(-chunkW / 2, 0);
CRect glowClip;
glowClip.IntersectRect(&glowChunk, &fillRc);
if (!glowClip.IsRectEmpty()) {
DrawRoundedRect(dc, glowClip, colorAccent2, RGB(140, 120, 255), 8);
}
CFont* oldFont = dc.SelectObject(&smallFont);
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(colorMutedText);
CRect labelRc = barRc;
labelRc.top = barRc.bottom + 6;
labelRc.bottom = labelRc.top + 18;
CString label = "Processing...";
dc.DrawText(label, &labelRc, DT_RIGHT | DT_VCENTER | DT_SINGLELINE);
dc.SelectObject(oldFont);
}
}
void CWaitDlg::DrawRoundedRect(CDC& dc, const CRect& rc, COLORREF fill, COLORREF outline, int radius) {
CBrush brush(fill);
CPen pen(PS_SOLID, 1, outline);
CBrush* oldBrush = dc.SelectObject(&brush);
CPen* oldPen = dc.SelectObject(&pen);
dc.RoundRect(&rc, CPoint(radius, radius));
dc.SelectObject(oldBrush);
dc.SelectObject(oldPen);
}