ravenrs_sheet/engine/cmd/edit/
selection.rs1use super::{
2 CellRef, EngineError, Json, RangeRef, Result, Session, bool_param, cell_param, current_region, json, jump, split_sheet, str_param, target_ranges,
3 target_sheet,
4};
5
6fn cell_selectable(wb: &crate::model::Workbook, sheet: &crate::model::Sheet, c: CellRef) -> bool {
10 let Some(p) = sheet.protection.as_ref() else { return true };
11 if wb.styles.get(sheet.style_id(c)).protection.locked { p.select_locked } else { p.select_unlocked }
12}
13
14pub(super) fn selection_set(s: &mut Session, p: &Json) -> Result<Json> {
15 if p.get("sheet").is_some() {
16 let i = target_sheet(s, p)?;
17 s.doc_mut()?.wb_switch_sheet(i);
18 } else if let Some(r) = str_param(p, "range").or_else(|| str_param(p, "cell"))
19 && let (Some(_), _) = split_sheet(r)
20 {
21 let i = target_sheet(s, p)?;
22 s.doc_mut()?.wb_switch_sheet(i);
23 }
24 let ranges = target_ranges(s, p)?;
25 let active = cell_param(s, p, "active")?;
26 let d = s.doc_mut()?;
27 let active = active.or_else(|| ranges.last().map(|r| r.start)).unwrap_or_default();
28 let mut sel = crate::engine::Selection { active, anchor: ranges.last().map(|r| r.start).unwrap_or(active), ranges };
29 if let Some(sh) = d.wb.active() {
30 sel.expand_merges(sh);
31 if !cell_selectable(&d.wb, sh, sel.active) {
32 return Err(EngineError::Other("The cell you are trying to change is on a protected sheet that does not allow selecting it.".into()));
33 }
34 }
35 d.selection = sel;
36 Ok(json!({"selection": d.selection.a1(), "active": d.selection.active.a1()}))
37}
38
39impl crate::engine::DocState {
40 pub fn wb_switch_sheet(&mut self, i: usize) {
42 if i >= self.wb.sheets.len() || i == self.wb.active_sheet() {
43 return;
44 }
45 let cur = self.wb.active_sheet();
46 if self.sheet_selections.len() < self.wb.sheets.len() {
47 self.sheet_selections.resize(self.wb.sheets.len(), crate::engine::Selection::default());
48 }
49 if let Some(slot) = self.sheet_selections.get_mut(cur) {
50 *slot = self.selection.clone();
51 }
52 self.wb.set_active_sheet(i);
54 self.selection = self.sheet_selections.get(i).cloned().unwrap_or_default();
55 }
56}
57
58pub(super) fn selection_move(s: &mut Session, p: &Json) -> Result<Json> {
59 let dr = p.get("dr").and_then(Json::as_i64).unwrap_or(0);
60 let dc = p.get("dc").and_then(Json::as_i64).unwrap_or(0);
61 let extend = bool_param(p, "extend").unwrap_or(false);
62 let jmp = bool_param(p, "jump").unwrap_or(false);
63 let d = s.doc_mut()?;
64 let Some(sheet) = d.wb.active() else { return Err(EngineError::NoDocument) };
65 let sel = &mut d.selection;
66 let from = if extend { sel.moving_end() } else { sel.active };
68 let mut to = if jmp && extend && (dr == 0) != (dc == 0) {
70 let probe = if dr != 0 { CellRef::new(from.row, sel.active.col) } else { CellRef::new(sel.active.row, from.col) };
74 let edge = jump(sheet, probe, dr.signum(), dc.signum());
75 if dr != 0 { CellRef::new(edge.row, from.col) } else { CellRef::new(from.row, edge.col) }
76 } else if jmp {
77 jump(sheet, from, dr.signum(), dc.signum())
78 } else {
79 let mut t = from.offset_clamped(dr, dc);
80 let mut guard = 0;
81 while (sheet.is_row_hidden(t.row) || sheet.is_col_hidden(t.col)) && guard < 100_000 {
82 let n = t.offset_clamped(dr.signum(), dc.signum());
83 if n == t {
84 break;
85 }
86 t = n;
87 guard += 1;
88 }
89 if !extend && let Some(m) = sheet.merge_at(from) {
90 if dr > 0 {
92 t = CellRef::new(m.end.offset_clamped(dr, 0).row, from.col);
93 } else if dc > 0 {
94 t = CellRef::new(from.row, m.end.offset_clamped(0, dc).col);
95 }
96 }
97 t
98 };
99 if let Some(m) = sheet.merge_at(to)
100 && !extend
101 {
102 to = m.start;
103 }
104 if !extend && !cell_selectable(&d.wb, sheet, to) {
105 let (sr, sc) = (dr.signum(), dc.signum());
107 let mut t = to;
108 let mut found = false;
109 if !jmp && (sr, sc) != (0, 0) {
110 for _ in 0..10_000 {
111 let n = t.offset_clamped(sr, sc);
112 if n == t {
113 break;
114 }
115 t = n;
116 if !sheet.is_row_hidden(t.row) && !sheet.is_col_hidden(t.col) && cell_selectable(&d.wb, sheet, t) {
117 found = true;
118 break;
119 }
120 }
121 }
122 if !found {
123 return Ok(json!({"selection": sel.a1(), "active": sel.active.a1()}));
124 }
125 to = t;
126 }
127 if extend {
128 sel.extend_to(to);
129 sel.expand_merges(sheet);
130 } else {
131 *sel = crate::engine::Selection::at(to);
132 if let Some(m) = sheet.merge_at(to) {
133 sel.ranges = vec![m];
134 }
135 }
136 Ok(json!({"selection": sel.a1(), "active": sel.active.a1()}))
137}
138
139pub(super) fn selection_next(s: &mut Session, p: &Json) -> Result<Json> {
140 let fwd = bool_param(p, "forward").unwrap_or(true);
141 let by_row = bool_param(p, "byRow").unwrap_or(false);
142 let d = s.doc_mut()?;
143 d.selection.cycle(fwd, by_row);
144 Ok(json!({"active": d.selection.active.a1()}))
145}
146
147pub(super) fn selection_next_corner(s: &mut Session, _: &Json) -> Result<Json> {
148 let d = s.doc_mut()?;
149 let Some(sheet) = d.wb.active() else { return Err(EngineError::NoDocument) };
150 let area = d.selection.current();
154 let stop = |c: CellRef| {
155 Some(sheet.merge_at(c).map_or(c, |m| m.start))
156 .filter(|c| area.contains(*c) && !sheet.is_row_hidden(c.row) && !sheet.is_col_hidden(c.col) && cell_selectable(&d.wb, sheet, *c))
157 };
158 let mut next = d.selection.clone();
159 next.next_corner_by(stop);
160 d.selection = next;
161 Ok(json!({"active": d.selection.active.a1(), "selection": d.selection.a1()}))
162}
163
164pub(super) fn select_all(s: &mut Session, _: &Json) -> Result<Json> {
165 let d = s.doc_mut()?;
166 let Some(sheet) = d.wb.active() else { return Err(EngineError::NoDocument) };
167 let region = current_region(sheet, d.selection.active);
168 let all = RangeRef::all();
169 let r = if region.is_single() || d.selection.current() == region { all } else { region };
170 let active = d.selection.active;
171 d.selection = crate::engine::Selection { active, anchor: r.start, ranges: vec![r] };
172 Ok(json!({"selection": d.selection.a1()}))
173}
174
175pub(super) fn select_region(s: &mut Session, _: &Json) -> Result<Json> {
176 let d = s.doc_mut()?;
177 let Some(sheet) = d.wb.active() else { return Err(EngineError::NoDocument) };
178 let r = current_region(sheet, d.selection.active);
179 let active = d.selection.active;
180 d.selection = crate::engine::Selection { active, anchor: r.start, ranges: vec![r] };
181 Ok(json!({"selection": d.selection.a1()}))
182}
183
184pub(super) fn select_row(s: &mut Session, _: &Json) -> Result<Json> {
185 let d = s.doc_mut()?;
186 let r = d.selection.current();
187 let active = d.selection.active;
188 d.selection = crate::engine::Selection { active, anchor: CellRef::new(r.start.row, 0), ranges: vec![RangeRef::rows(r.start.row, r.end.row)] };
189 Ok(json!({"selection": d.selection.a1()}))
190}
191
192pub(super) fn select_col(s: &mut Session, _: &Json) -> Result<Json> {
193 let d = s.doc_mut()?;
194 let r = d.selection.current();
195 let active = d.selection.active;
196 d.selection = crate::engine::Selection { active, anchor: CellRef::new(0, r.start.col), ranges: vec![RangeRef::cols(r.start.col, r.end.col)] };
197 Ok(json!({"selection": d.selection.a1()}))
198}