Skip to main content

pacenotes_installer\wizard\scene/
progress_page.rs

1//! What scenes 4b and 6 share: they are the same screen at two moments of the
2//! run, with the same control ids.
3
4use windows_sys::Win32::Foundation::{HWND, LPARAM, WPARAM};
5use windows_sys::Win32::UI::Controls as ctl;
6use windows_sys::Win32::UI::WindowsAndMessaging as wm;
7
8/// Progress bar steps; bytes would overflow `PBM_SETRANGE32`'s i32 past 2 GB.
9pub const PROGRESS_SCALE: u32 = 1000;
10
11pub unsafe fn init_progress(dialog: HWND) {
12    wm::SendDlgItemMessageW(
13        dialog,
14        crate::wizard::control_ids::IDC_S6_PROGRESS as i32,
15        ctl::PBM_SETRANGE32,
16        0,
17        PROGRESS_SCALE as LPARAM,
18    );
19}
20
21pub unsafe fn set_progress(dialog: HWND, position: u32) {
22    wm::SendDlgItemMessageW(
23        dialog,
24        crate::wizard::control_ids::IDC_S6_PROGRESS as i32,
25        ctl::PBM_SETPOS,
26        position as WPARAM,
27        0,
28    );
29}
30
31pub unsafe fn show_progress(
32    dialog: HWND,
33    state: &mut crate::wizard::state::WizardState,
34    progress: &crate::worker::Progress,
35) {
36    let phase = match progress.phase {
37        crate::worker::Phase::Download => crate::i18n::IDS_S6_DOWNLOADING,
38        crate::worker::Phase::Checking => crate::i18n::IDS_S6_CHECKING,
39        crate::worker::Phase::Uninstall => crate::i18n::IDS_S6_UNINSTALLING,
40        crate::worker::Phase::Backup => crate::i18n::IDS_S6_BACKUP,
41        crate::worker::Phase::Install => crate::i18n::IDS_S6_INSTALLING,
42        crate::worker::Phase::Configure => crate::i18n::IDS_S6_CONFIGURING,
43    };
44    crate::ui::set_text(dialog, crate::wizard::control_ids::IDC_S6_PHASE, phase);
45
46    let item = if progress.item.is_empty() {
47        String::new()
48    } else {
49        format!(
50            "{} — {}",
51            progress.item,
52            pacenotes_installer::size::human_progress(progress.item_done, progress.item_size)
53        )
54    };
55    crate::ui::set_text_raw(dialog, crate::wizard::control_ids::IDC_S6_ITEM, &item);
56
57    if let Some(position) = progress
58        .done
59        .saturating_mul(PROGRESS_SCALE as u64)
60        .checked_div(progress.total)
61    {
62        set_progress(dialog, position as u32);
63    }
64    if let Some(line) = &progress.log {
65        log_line(dialog, state, line);
66    }
67}
68
69/// Always vetoes the close: a confirmed Cancel raises the flag, and the
70/// worker's `Cancelled` then walks the wizard on to the summary. Scene 6
71/// disables the button, since the engine cannot stop between files.
72pub unsafe fn on_cancel(dialog: HWND) -> isize {
73    let Some(state) = crate::wizard::page::state(dialog) else {
74        return crate::wizard::page::reply(dialog, 0);
75    };
76    if state.outcome.is_some() {
77        return crate::wizard::page::reply(dialog, 0);
78    }
79    let veto_close = 1;
80    if !crate::ui::confirm(
81        dialog,
82        &crate::i18n::load_string(crate::i18n::IDS_CANCEL_CONFIRM),
83    ) {
84        return crate::wizard::page::reply(dialog, veto_close);
85    }
86    state
87        .cancel
88        .store(true, std::sync::atomic::Ordering::Relaxed);
89    crate::ui::set_text(
90        dialog,
91        crate::wizard::control_ids::IDC_S6_PHASE,
92        crate::i18n::IDS_S6_CANCELLING,
93    );
94    log_line(dialog, state, "stopping at the user's request");
95    crate::wizard::page::reply(dialog, veto_close)
96}
97
98/// Shows the finished run's state, then has the sheet move to `next` by pressing
99/// Next, or by page id when the next page is not the one after this.
100pub unsafe fn finish_page(dialog: HWND, phase: u16, next: Option<u16>) {
101    crate::ui::set_text(dialog, crate::wizard::control_ids::IDC_S6_PHASE, phase);
102    crate::ui::set_text_raw(dialog, crate::wizard::control_ids::IDC_S6_ITEM, "");
103    set_progress(dialog, PROGRESS_SCALE);
104    let sheet = crate::wizard::page::sheet(dialog);
105    crate::ui::enable(sheet, crate::ui::IDCANCEL, false);
106    crate::wizard::page::set_buttons(dialog, ctl::PSWIZB_NEXT);
107    match next {
108        None => wm::PostMessageW(sheet, ctl::PSM_PRESSBUTTON, ctl::PSBTN_NEXT as WPARAM, 0),
109        Some(page) => wm::PostMessageW(sheet, ctl::PSM_SETCURSELID, 0, page as LPARAM),
110    };
111}
112
113/// Appends to the run log and adds the line to the pane, where the page has
114/// one; `show_log` has brought the pane up to date when the page appeared.
115pub unsafe fn log_line(dialog: HWND, state: &mut crate::wizard::state::WizardState, text: &str) {
116    state.log.line(text);
117    crate::ui::append_line(dialog, crate::wizard::control_ids::IDC_S6_LOG, text);
118}
119
120/// The whole log so far, for a page coming into view: lines are logged from
121/// pages that have no pane, so this page has not seen them.
122pub unsafe fn show_log(dialog: HWND, state: &crate::wizard::state::WizardState) {
123    crate::ui::set_text_raw(
124        dialog,
125        crate::wizard::control_ids::IDC_S6_LOG,
126        &state.log.text(),
127    );
128    crate::ui::scroll_to_end(dialog, crate::wizard::control_ids::IDC_S6_LOG);
129}