/* =========================================================================== 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 . 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); }