mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-02 07:06:48 -06:00
engine: pageLayout.printTitles only touches the axes it is given
print_titles read both rows and cols as Options and assigned both
unconditionally, so {"rows":"2:2"} set title_cols = None and
{"cols":"A:A"} set title_rows = None: the repeated rows or columns
disappeared from every printed page with no message, and there was no
way to set or clear just one axis. The sibling pageLayout.pageSetup
already distinguishes "key absent" from "key present but empty"; read
both axes the same way — absent leaves the axis alone, empty/null clears
it, anything else that is not a range is an error rather than a silent
wipe.
Regression test: print_titles_keeps_the_axis_it_is_not_given — on main
the rows-only call clears the columns; with the fix each axis is touched
only when named, an explicit empty value still clears, and a bad range
errors.
This commit is contained in:
@@ -232,13 +232,31 @@ fn print_area(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
}
|
||||
|
||||
fn print_titles(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
let rows = str_param(p, "rows").and_then(RangeRef::parse).map(|r| (r.start.row, r.end.row));
|
||||
let cols = str_param(p, "cols").and_then(RangeRef::parse).map(|r| (r.start.col, r.end.col));
|
||||
// An absent key leaves that axis alone, and an empty or null value clears it — the same
|
||||
// reading `pageLayout.pageSetup` gives `titleRows`/`titleCols`. Assigning `None` for a
|
||||
// key that was never sent used to wipe the other axis.
|
||||
let axis = |key: &str, is_cols: bool| -> Result<Option<Option<(u32, u32)>>> {
|
||||
match p.get(key) {
|
||||
None => Ok(None),
|
||||
Some(Json::Null) => Ok(Some(None)),
|
||||
Some(Json::String(t)) if t.trim().is_empty() => Ok(Some(None)),
|
||||
Some(Json::String(t)) => match RangeRef::parse(t) {
|
||||
Some(r) => Ok(Some(Some(if is_cols { (r.start.col, r.end.col) } else { (r.start.row, r.end.row) }))),
|
||||
None => Err(bad("pageLayout.printTitles", format!("`{key}` is not a range: `{t}`"))),
|
||||
},
|
||||
Some(_) => Err(bad("pageLayout.printTitles", format!("`{key}` must be text like \"1:2\" or \"A:A\""))),
|
||||
}
|
||||
};
|
||||
let (rows, cols) = (axis("rows", false)?, axis("cols", true)?);
|
||||
edit(s, |cx| {
|
||||
let si = cx.sheet_index();
|
||||
let pr = &mut cx.sheet_mut(si)?.print;
|
||||
pr.title_rows = rows;
|
||||
pr.title_cols = cols;
|
||||
if let Some(t) = rows {
|
||||
pr.title_rows = t;
|
||||
}
|
||||
if let Some(t) = cols {
|
||||
pr.title_cols = t;
|
||||
}
|
||||
Ok(Json::Null)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2925,6 +2925,33 @@ fn notes_comments_and_links_survive_a_json_save() {
|
||||
assert_eq!(after.cells, before.cells);
|
||||
}
|
||||
|
||||
/// Page Layout ▸ Print Titles: naming one axis must not clear the other. `print_titles` read
|
||||
/// both `rows` and `cols` into `Option`s and assigned both unconditionally, so
|
||||
/// `{"rows":"2:2"}` silently dropped the repeated columns from every printed page.
|
||||
#[test]
|
||||
fn print_titles_keeps_the_axis_it_is_not_given() {
|
||||
let mut s = s();
|
||||
s.execute("pageLayout.printTitles", json!({"rows": "1:1", "cols": "A:A"})).unwrap();
|
||||
let titles = |s: &Session| {
|
||||
let d = s.doc().unwrap();
|
||||
let sh = d.wb.active().unwrap();
|
||||
(sh.print.title_rows, sh.print.title_cols)
|
||||
};
|
||||
assert_eq!(titles(&s), (Some((0, 0)), Some((0, 0))));
|
||||
// Only rows: the columns stay.
|
||||
s.execute("pageLayout.printTitles", json!({"rows": "2:2"})).unwrap();
|
||||
assert_eq!(titles(&s), (Some((1, 1)), Some((0, 0))), "rows alone must not clear the columns");
|
||||
// Only columns: the rows stay.
|
||||
s.execute("pageLayout.printTitles", json!({"cols": "B:B"})).unwrap();
|
||||
assert_eq!(titles(&s), (Some((1, 1)), Some((1, 1))), "columns alone must not clear the rows");
|
||||
// An empty value clears that axis on purpose.
|
||||
s.execute("pageLayout.printTitles", json!({"cols": ""})).unwrap();
|
||||
assert_eq!(titles(&s), (Some((1, 1)), None));
|
||||
// A bad range is an error, not a silent wipe.
|
||||
assert!(s.execute("pageLayout.printTitles", json!({"rows": "not a range"})).is_err());
|
||||
assert_eq!(titles(&s), (Some((1, 1)), None));
|
||||
}
|
||||
|
||||
/// Insert › Link sorts a target as Excel's dialog does: a sheet reference, a defined name or `#…` is
|
||||
/// a place in this document, anything else an existing file or web page. A file saved as a
|
||||
/// `location` opens nothing in Excel (#800).
|
||||
|
||||
Reference in New Issue
Block a user