michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "resource.h" michael@0: #include "progressui.h" michael@0: #include "readstrings.h" michael@0: #include "errors.h" michael@0: michael@0: #define TIMER_ID 1 michael@0: #define TIMER_INTERVAL 100 michael@0: michael@0: #define RESIZE_WINDOW(hwnd, extrax, extray) \ michael@0: { \ michael@0: RECT windowSize; \ michael@0: GetWindowRect(hwnd, &windowSize); \ michael@0: SetWindowPos(hwnd, 0, 0, 0, windowSize.right - windowSize.left + extrax, \ michael@0: windowSize.bottom - windowSize.top + extray, \ michael@0: SWP_NOMOVE | SWP_NOZORDER); \ michael@0: } michael@0: michael@0: #define MOVE_WINDOW(hwnd, dx, dy) \ michael@0: { \ michael@0: RECT rc; \ michael@0: POINT pt; \ michael@0: GetWindowRect(hwnd, &rc); \ michael@0: pt.x = rc.left; \ michael@0: pt.y = rc.top; \ michael@0: ScreenToClient(GetParent(hwnd), &pt); \ michael@0: SetWindowPos(hwnd, 0, pt.x + dx, pt.y + dy, 0, 0, \ michael@0: SWP_NOSIZE | SWP_NOZORDER); \ michael@0: } michael@0: michael@0: static float sProgress; // between 0 and 100 michael@0: static BOOL sQuit = FALSE; michael@0: static BOOL sIndeterminate = FALSE; michael@0: static StringTable sUIStrings; michael@0: michael@0: static BOOL michael@0: GetStringsFile(WCHAR filename[MAX_PATH]) michael@0: { michael@0: if (!GetModuleFileNameW(nullptr, filename, MAX_PATH)) michael@0: return FALSE; michael@0: michael@0: WCHAR *dot = wcsrchr(filename, '.'); michael@0: if (!dot || wcsicmp(dot + 1, L"exe")) michael@0: return FALSE; michael@0: michael@0: wcscpy(dot + 1, L"ini"); michael@0: return TRUE; michael@0: } michael@0: michael@0: static void michael@0: UpdateDialog(HWND hDlg) michael@0: { michael@0: int pos = int(sProgress + 0.5f); michael@0: HWND hWndPro = GetDlgItem(hDlg, IDC_PROGRESS); michael@0: SendMessage(hWndPro, PBM_SETPOS, pos, 0L); michael@0: } michael@0: michael@0: // The code in this function is from MSDN: michael@0: // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/dialogboxes/usingdialogboxes.asp michael@0: static void michael@0: CenterDialog(HWND hDlg) michael@0: { michael@0: RECT rc, rcOwner, rcDlg; michael@0: michael@0: // Get the owner window and dialog box rectangles. michael@0: HWND desktop = GetDesktopWindow(); michael@0: michael@0: GetWindowRect(desktop, &rcOwner); michael@0: GetWindowRect(hDlg, &rcDlg); michael@0: CopyRect(&rc, &rcOwner); michael@0: michael@0: // Offset the owner and dialog box rectangles so that michael@0: // right and bottom values represent the width and michael@0: // height, and then offset the owner again to discard michael@0: // space taken up by the dialog box. michael@0: michael@0: OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); michael@0: OffsetRect(&rc, -rc.left, -rc.top); michael@0: OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); michael@0: michael@0: // The new position is the sum of half the remaining michael@0: // space and the owner's original position. michael@0: michael@0: SetWindowPos(hDlg, michael@0: HWND_TOP, michael@0: rcOwner.left + (rc.right / 2), michael@0: rcOwner.top + (rc.bottom / 2), michael@0: 0, 0, // ignores size arguments michael@0: SWP_NOSIZE); michael@0: } michael@0: michael@0: static void michael@0: InitDialog(HWND hDlg) michael@0: { michael@0: WCHAR szwTitle[MAX_TEXT_LEN]; michael@0: WCHAR szwInfo[MAX_TEXT_LEN]; michael@0: michael@0: MultiByteToWideChar(CP_UTF8, 0, sUIStrings.title, -1, szwTitle, michael@0: sizeof(szwTitle)/sizeof(szwTitle[0])); michael@0: MultiByteToWideChar(CP_UTF8, 0, sUIStrings.info, -1, szwInfo, michael@0: sizeof(szwInfo)/sizeof(szwInfo[0])); michael@0: michael@0: SetWindowTextW(hDlg, szwTitle); michael@0: SetWindowTextW(GetDlgItem(hDlg, IDC_INFO), szwInfo); michael@0: michael@0: // Set dialog icon michael@0: HICON hIcon = LoadIcon(GetModuleHandle(nullptr), michael@0: MAKEINTRESOURCE(IDI_DIALOG)); michael@0: if (hIcon) michael@0: SendMessage(hDlg, WM_SETICON, ICON_BIG, (LPARAM) hIcon); michael@0: michael@0: HWND hWndPro = GetDlgItem(hDlg, IDC_PROGRESS); michael@0: SendMessage(hWndPro, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); michael@0: if (sIndeterminate) { michael@0: LONG_PTR val = GetWindowLongPtr(hWndPro, GWL_STYLE); michael@0: SetWindowLongPtr(hWndPro, GWL_STYLE, val|PBS_MARQUEE); michael@0: SendMessage(hWndPro,(UINT) PBM_SETMARQUEE,(WPARAM) TRUE,(LPARAM)50 ); michael@0: } michael@0: michael@0: // Resize the dialog to fit all of the text if necessary. michael@0: RECT infoSize, textSize; michael@0: HWND hWndInfo = GetDlgItem(hDlg, IDC_INFO); michael@0: michael@0: // Get the control's font for calculating the new size for the control michael@0: HDC hDCInfo = GetDC(hWndInfo); michael@0: HFONT hInfoFont, hOldFont; michael@0: hInfoFont = (HFONT)SendMessage(hWndInfo, WM_GETFONT, 0, 0); michael@0: michael@0: if (hInfoFont) michael@0: hOldFont = (HFONT)SelectObject(hDCInfo, hInfoFont); michael@0: michael@0: // Measure the space needed for the text on a single line. DT_CALCRECT means michael@0: // nothing is drawn. michael@0: if (DrawText(hDCInfo, szwInfo, -1, &textSize, michael@0: DT_CALCRECT | DT_NOCLIP | DT_SINGLELINE)) { michael@0: GetClientRect(hWndInfo, &infoSize); michael@0: SIZE extra; michael@0: // Calculate the additional space needed for the text by subtracting from michael@0: // the rectangle returned by DrawText the existing client rectangle's width michael@0: // and height. michael@0: extra.cx = (textSize.right - textSize.left) - \ michael@0: (infoSize.right - infoSize.left); michael@0: extra.cy = (textSize.bottom - textSize.top) - \ michael@0: (infoSize.bottom - infoSize.top); michael@0: if (extra.cx < 0) michael@0: extra.cx = 0; michael@0: if (extra.cy < 0) michael@0: extra.cy = 0; michael@0: if ((extra.cx > 0) || (extra.cy > 0)) { michael@0: RESIZE_WINDOW(hDlg, extra.cx, extra.cy); michael@0: RESIZE_WINDOW(hWndInfo, extra.cx, extra.cy); michael@0: RESIZE_WINDOW(hWndPro, extra.cx, 0); michael@0: MOVE_WINDOW(hWndPro, 0, extra.cy); michael@0: } michael@0: } michael@0: michael@0: if (hOldFont) michael@0: SelectObject(hDCInfo, hOldFont); michael@0: michael@0: ReleaseDC(hWndInfo, hDCInfo); michael@0: michael@0: CenterDialog(hDlg); // make dialog appear in the center of the screen michael@0: michael@0: SetTimer(hDlg, TIMER_ID, TIMER_INTERVAL, nullptr); michael@0: } michael@0: michael@0: // Message handler for update dialog. michael@0: static LRESULT CALLBACK michael@0: DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) michael@0: { michael@0: switch (message) michael@0: { michael@0: case WM_INITDIALOG: michael@0: InitDialog(hDlg); michael@0: return TRUE; michael@0: michael@0: case WM_TIMER: michael@0: if (sQuit) { michael@0: EndDialog(hDlg, 0); michael@0: } else { michael@0: UpdateDialog(hDlg); michael@0: } michael@0: return TRUE; michael@0: michael@0: case WM_COMMAND: michael@0: return TRUE; michael@0: } michael@0: return FALSE; michael@0: } michael@0: michael@0: int michael@0: InitProgressUI(int *argc, NS_tchar ***argv) michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Initializes the progress UI strings michael@0: * michael@0: * @return 0 on success, -1 on error michael@0: */ michael@0: int michael@0: InitProgressUIStrings() { michael@0: // If we do not have updater.ini, then we should not bother showing UI. michael@0: WCHAR filename[MAX_PATH]; michael@0: if (!GetStringsFile(filename)) { michael@0: return -1; michael@0: } michael@0: michael@0: if (_waccess(filename, 04)) { michael@0: return -1; michael@0: } michael@0: michael@0: // If the updater.ini doesn't have the required strings, then we should not michael@0: // bother showing UI. michael@0: if (ReadStrings(filename, &sUIStrings) != OK) { michael@0: return -1; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: int michael@0: ShowProgressUI(bool indeterminate, bool initUIStrings) michael@0: { michael@0: sIndeterminate = indeterminate; michael@0: if (!indeterminate) { michael@0: // Only show the Progress UI if the process is taking a significant amount of michael@0: // time where a significant amount of time is defined as .5 seconds after michael@0: // ShowProgressUI is called sProgress is less than 70. michael@0: Sleep(500); michael@0: michael@0: if (sQuit || sProgress > 70.0f) michael@0: return 0; michael@0: } michael@0: michael@0: if (initUIStrings && InitProgressUIStrings() == -1) { michael@0: return -1; michael@0: } michael@0: michael@0: INITCOMMONCONTROLSEX icc = { michael@0: sizeof(INITCOMMONCONTROLSEX), michael@0: ICC_PROGRESS_CLASS michael@0: }; michael@0: InitCommonControlsEx(&icc); michael@0: michael@0: DialogBox(GetModuleHandle(nullptr), michael@0: MAKEINTRESOURCE(IDD_DIALOG), nullptr, michael@0: (DLGPROC) DialogProc); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: QuitProgressUI() michael@0: { michael@0: sQuit = TRUE; michael@0: } michael@0: michael@0: void michael@0: UpdateProgressUI(float progress) michael@0: { michael@0: sProgress = progress; // 32-bit writes are atomic michael@0: }