mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-29 21:26:48 -06:00
This commit is contained in:
@@ -373,6 +373,12 @@ fn range_fill(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
|
||||
// ---------------------------------------------------------------- selection
|
||||
|
||||
/// Whether the cell may be selected: a protected sheet can forbid selecting locked and/or unlocked cells.
|
||||
fn cell_selectable(wb: &gridcraft_model::Workbook, sheet: &gridcraft_model::Sheet, c: CellRef) -> bool {
|
||||
let Some(p) = sheet.protection.as_ref() else { return true };
|
||||
if wb.styles.get(sheet.style_id(c)).protection.locked { p.select_locked } else { p.select_unlocked }
|
||||
}
|
||||
|
||||
fn selection_set(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
if p.get("sheet").is_some() {
|
||||
let i = target_sheet(s, p)?;
|
||||
@@ -390,6 +396,9 @@ fn selection_set(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
let mut sel = crate::Selection { active, anchor: ranges.last().map(|r| r.start).unwrap_or(active), ranges };
|
||||
if let Some(sh) = d.wb.active() {
|
||||
sel.expand_merges(sh);
|
||||
if !cell_selectable(&d.wb, sh, sel.active) {
|
||||
return Err(EngineError::Other("The cell you are trying to change is on a protected sheet that does not allow selecting it.".into()));
|
||||
}
|
||||
}
|
||||
d.selection = sel;
|
||||
Ok(json!({"selection": d.selection.a1(), "active": d.selection.active.a1()}))
|
||||
@@ -458,6 +467,29 @@ fn selection_move(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
{
|
||||
to = m.start;
|
||||
}
|
||||
if !extend && !cell_selectable(&d.wb, sheet, to) {
|
||||
// Step on to the next selectable cell in the same direction; stay put if there is none.
|
||||
let (sr, sc) = (dr.signum(), dc.signum());
|
||||
let mut t = to;
|
||||
let mut found = false;
|
||||
if !jmp && (sr, sc) != (0, 0) {
|
||||
for _ in 0..10_000 {
|
||||
let n = t.offset_clamped(sr, sc);
|
||||
if n == t {
|
||||
break;
|
||||
}
|
||||
t = n;
|
||||
if !sheet.is_row_hidden(t.row) && !sheet.is_col_hidden(t.col) && cell_selectable(&d.wb, sheet, t) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return Ok(json!({"selection": sel.a1(), "active": sel.active.a1()}));
|
||||
}
|
||||
to = t;
|
||||
}
|
||||
if extend {
|
||||
sel.extend_to(to);
|
||||
sel.expand_merges(sheet);
|
||||
|
||||
@@ -139,11 +139,15 @@ fn protect_sheet(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
return Err(EngineError::Other("The sheet is protected with a password. Unprotect it first (Review › Unprotect Sheet).".into()));
|
||||
}
|
||||
let b = |k: &str| bool_param(p, k).unwrap_or(false);
|
||||
// Selection rights default to what the sheet has now (a file may forbid selecting locked cells).
|
||||
let current = s.doc()?.wb.sheet(sheet).and_then(|sh| sh.protection.clone());
|
||||
let select_locked = bool_param(p, "selectLockedCells").unwrap_or_else(|| current.as_ref().is_none_or(|c| c.select_locked));
|
||||
let select_unlocked = bool_param(p, "selectUnlockedCells").unwrap_or_else(|| current.as_ref().is_none_or(|c| c.select_unlocked));
|
||||
let prot = SheetProtection {
|
||||
password: None,
|
||||
legacy_password_hash: None,
|
||||
select_locked: true,
|
||||
select_unlocked: true,
|
||||
select_locked,
|
||||
select_unlocked,
|
||||
format_cells: b("formatCells"),
|
||||
format_columns: b("formatColumns"),
|
||||
format_rows: b("formatRows"),
|
||||
@@ -399,6 +403,26 @@ mod tests {
|
||||
assert_eq!(at(&s), (0, "A1".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protection_that_forbids_selecting_locked_cells_is_enforced_and_kept() {
|
||||
let mut s = book();
|
||||
s.execute("home.lockCell", json!({"range": "B2", "on": false})).unwrap();
|
||||
s.execute("review.protectSheet", json!({"selectLockedCells": false})).unwrap();
|
||||
// Locked cells can't be selected; unlocked ones can.
|
||||
assert!(s.execute("selection.set", json!({"cell": "A1"})).is_err());
|
||||
s.execute("selection.set", json!({"cell": "B2"})).unwrap();
|
||||
// Arrow keys skip locked cells, and stay put when nothing selectable is left.
|
||||
s.execute("selection.move", json!({"dr": 0, "dc": 1})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().selection.active.a1(), "B2");
|
||||
// Re-protecting without the flag keeps the sheet's selection rights.
|
||||
s.execute("review.unprotectSheet", json!({})).unwrap();
|
||||
s.execute("review.protectSheet", json!({"selectLockedCells": false})).unwrap();
|
||||
s.execute("review.protectSheet", json!({"sort": true})).unwrap();
|
||||
assert!(!s.doc().unwrap().wb.active().unwrap().protection.clone().unwrap().select_locked);
|
||||
s.execute("review.unprotectSheet", json!({})).unwrap();
|
||||
s.execute("selection.set", json!({"cell": "A1"})).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sheet_passwords_use_excel_hashes_and_old_json_hashes_still_verify() {
|
||||
let mut s = book();
|
||||
|
||||
Reference in New Issue
Block a user