1use crate::base::RangeRef;
4use crate::model::Orientation;
5use serde_json::{Value as Json, json};
6
7use super::{
8 CommandSpec, EngineError, Result, Session, always, bad, bool_param, cell_param, cell_param_on, cmd, edit, f64_param, has_doc, str_param,
9 target_range, target_sheet, u32_param,
10};
11
12pub fn specs() -> Vec<CommandSpec> {
13 vec![
14 cmd!(
15 "view.freezePanes",
16 "Freeze Panes",
17 ["View", "Window", "Freeze Panes"],
18 None,
19 "{sheet?, cell?: \"B2\" (rows above and columns left of it freeze); without `cell`, unfreezes a frozen sheet}",
20 has_doc,
21 freeze_panes
22 ),
23 cmd!("view.freezeTopRow", "Freeze Top Row", ["View", "Window", "Freeze Panes"], None, "{sheet?}", has_doc, |s, p| set_freeze(
24 s,
25 target_sheet(s, p)?,
26 Some((1, 0))
27 )),
28 cmd!("view.freezeFirstColumn", "Freeze First Column", ["View", "Window", "Freeze Panes"], None, "{sheet?}", has_doc, |s, p| set_freeze(
29 s,
30 target_sheet(s, p)?,
31 Some((0, 1))
32 )),
33 cmd!("view.unfreezePanes", "Unfreeze Panes", ["View", "Window", "Freeze Panes"], None, "{sheet?}", has_doc, |s, p| set_freeze(
34 s,
35 target_sheet(s, p)?,
36 None
37 )),
38 cmd!("view.gridlines", "Gridlines", ["View", "Show"], None, "{on?}", has_doc, |s, p| sheet_flag(s, p, "gridlines")),
39 cmd!("view.headings", "Headings", ["View", "Show"], None, "{on?}", has_doc, |s, p| sheet_flag(s, p, "headings")),
40 cmd!("view.showZeros", "Show Zeros", [], None, "{on?}", has_doc, |s, p| sheet_flag(s, p, "zeros")),
41 cmd!("view.showOutlineSymbols", "Show Outline Symbols", [], Some("Ctrl+8"), "{on?}", has_doc, |s, p| sheet_flag(s, p, "outlineSymbols")),
42 cmd!("view.showObjects", "Show Objects", [], Some("Ctrl+6"), "{on?}", has_doc, show_objects),
43 cmd!(
44 "view.showSheetTabs",
45 "Show Sheet Tabs",
46 [],
47 None,
48 "{on?} (File › Options › Advanced, this workbook; toggles without `on`)",
49 has_doc,
50 |s, p| { workbook_view_flag(s, p, "sheetTabs") }
51 ),
52 cmd!(
53 "view.showHorizontalScrollBar",
54 "Show Horizontal Scroll Bar",
55 [],
56 None,
57 "{on?} (this workbook; toggles without `on`)",
58 has_doc,
59 |s, p| { workbook_view_flag(s, p, "horizontal") }
60 ),
61 cmd!("view.showVerticalScrollBar", "Show Vertical Scroll Bar", [], None, "{on?} (this workbook; toggles without `on`)", has_doc, |s, p| {
62 workbook_view_flag(s, p, "vertical")
63 }),
64 cmd!("view.rightToLeft", "Sheet Right-to-Left", ["Page Layout", "Sheet Options"], None, "{on?}", has_doc, |s, p| sheet_flag(s, p, "rtl")),
65 cmd!("view.zoom", "Zoom", ["View", "Zoom"], None, "{percent: 10..400}", has_doc, zoom),
66 cmd!("view.zoom100", "100%", ["View", "Zoom"], None, "{}", has_doc, |s, _| zoom(s, &json!({"percent": 100}))),
67 cmd!(query "view.formulaBar", "Formula Bar", ["View", "Show"], None, "{on?: bool}", always, |s, p| {
68 s.prefs.formula_bar = bool_param(p, "on").unwrap_or(!s.prefs.formula_bar);
69 Ok(json!({"on": s.prefs.formula_bar}))
70 }),
71 cmd!(
72 query "view.statusBar",
73 "Customize Status Bar",
74 [],
75 None,
76 "{average?, count?, numericalCount?, minimum?, maximum?, sum?: bool (the statistics shown for a selection; returns all six)}",
77 always,
78 status_bar
79 ),
80 cmd!("view.zoomToSelection", "Zoom to Selection", ["View", "Zoom"], None, "{viewWidth?, viewHeight?}", has_doc, zoom_selection),
81 cmd!("pageLayout.orientation", "Orientation", ["Page Layout", "Page Setup"], None, "{orientation: portrait|landscape}", has_doc, orientation),
82 cmd!(
83 "pageLayout.size",
84 "Size",
85 ["Page Layout", "Page Setup"],
86 None,
87 "{paper: Letter|Legal|A4|A3|A5|A6|Tabloid|Executive|Ledger|B4|B5|B6|B4 (ISO)|B5 (ISO)|B6 (ISO)|Japanese Postcard|Japanese Double Postcard|Japanese Envelope #3|Japanese Envelope #4|Japanese Envelope Kaku #2|Japanese Envelope Kaku #3 (B4/B5/B6 are the JIS sizes)}",
88 has_doc,
89 paper
90 ),
91 cmd!(
92 "pageLayout.margins",
93 "Margins",
94 ["Page Layout", "Page Setup"],
95 None,
96 "{preset?: normal|wide|narrow, left?, right?, top?, bottom?, header?, footer? (inches)}",
97 has_doc,
98 margins
99 ),
100 cmd!(
101 "pageLayout.printArea",
102 "Set Print Area",
103 ["Page Layout", "Page Setup", "Print Area"],
104 None,
105 "{range?, clear?: bool}",
106 has_doc,
107 print_area
108 ),
109 cmd!(
110 "pageLayout.printTitles",
111 "Print Titles",
112 ["Page Layout", "Page Setup"],
113 None,
114 "{rows?: \"1:1\", cols?: \"A:A\"}",
115 has_doc,
116 print_titles
117 ),
118 cmd!(
119 "pageLayout.scaleToFit",
120 "Scale to Fit",
121 ["Page Layout", "Scale to Fit"],
122 None,
123 "{scale?: 100, width?: pages, height?: pages}",
124 has_doc,
125 scale_fit
126 ),
127 cmd!("pageLayout.printGridlines", "Print Gridlines", ["Page Layout", "Sheet Options"], None, "{on?}", has_doc, |s, p| sheet_flag(
128 s,
129 p,
130 "printGridlines"
131 )),
132 cmd!("pageLayout.printHeadings", "Print Headings", ["Page Layout", "Sheet Options"], None, "{on?}", has_doc, |s, p| sheet_flag(
133 s,
134 p,
135 "printHeadings"
136 )),
137 cmd!(
138 "pageLayout.breaks",
139 "Breaks",
140 ["Page Layout", "Page Setup"],
141 None,
142 "{insert?: bool, remove?: bool, reset?: bool, cell?}",
143 has_doc,
144 breaks
145 ),
146 cmd!(
147 "pageLayout.headerFooter",
148 "Header & Footer",
149 ["Insert", "Text"],
150 None,
151 "{sheet?, header?: \"&C&P\", footer?: \"&CPage &P of &N\", differentFirst?: bool, differentOddEven?: bool, scaleWithDoc?: bool, alignWithMargins?: bool, firstHeader?, firstFooter?, evenHeader?, evenFooter?} or one section: {sheet?, page?: odd|even|first, part: header|footer, section: left|center|right, text: \"Page &[Page] of &[Pages]\"} (field names or codes) → {header, footer, differentFirst, differentOddEven, scaleWithDoc, alignWithMargins, firstHeader, firstFooter, evenHeader, evenFooter}",
152 has_doc,
153 header_footer
154 ),
155 cmd!(
156 "pageLayout.theme",
157 "Themes",
158 ["Page Layout", "Themes"],
159 None,
160 "{name: Craft|Slate|Meadow|Ember|Ocean|Orchid|Graphite, colors?: [12 hex], majorFont?, minorFont?}",
161 has_doc,
162 theme
163 ),
164 ]
165}
166
167fn freeze_panes(s: &mut Session, p: &Json) -> Result<Json> {
168 let si = target_sheet(s, p)?;
169 let d = s.doc()?;
170 let sh = d.wb.sheet(si).ok_or(EngineError::NoDocument)?;
171 if sh.freeze.is_some() && p.get("cell").is_none() {
172 return set_freeze(s, si, None);
173 }
174 let at = match cell_param_on(s, p, "cell", si)? {
175 Some(c) => c,
176 None if si == d.wb.active_sheet() => d.selection.active,
177 None => return Err(bad("view.freezePanes", "missing `cell`: the selection is on the active sheet")),
179 };
180 let top = sh.view_top_left;
181 let rows = at.row.saturating_sub(top.row);
182 let cols = at.col.saturating_sub(top.col);
183 let _ = (rows, cols);
184 let (rows, cols) = (at.row, at.col);
185 set_freeze(s, si, if rows == 0 && cols == 0 { None } else { Some((rows, cols)) })
186}
187
188fn set_freeze(s: &mut Session, si: usize, f: Option<(u32, u32)>) -> Result<Json> {
189 edit(s, |cx| {
190 cx.sheet_mut(si)?.freeze = f;
191 Ok(json!({"freeze": f}))
192 })
193}
194
195fn sheet_flag(s: &mut Session, p: &Json, which: &str) -> Result<Json> {
196 edit(s, |cx| {
197 let si = cx.sheet_index();
198 let sh = cx.sheet_mut(si)?;
199 let slot = match which {
200 "gridlines" => &mut sh.show_gridlines,
201 "headings" => &mut sh.show_headings,
202 "zeros" => &mut sh.show_zeros,
203 "outlineSymbols" => &mut sh.show_outline_symbols,
204 "rtl" => &mut sh.right_to_left,
205 "printGridlines" => &mut sh.print.gridlines,
206 _ => &mut sh.print.headings,
207 };
208 *slot = bool_param(p, "on").unwrap_or(!*slot);
209 Ok(json!({"on": *slot}))
210 })
211}
212
213fn workbook_view_flag(s: &mut Session, p: &Json, which: &str) -> Result<Json> {
216 edit(s, |cx| {
217 let view = &mut cx.wb.view;
218 let slot = match which {
219 "sheetTabs" => &mut view.show_sheet_tabs,
220 "horizontal" => &mut view.show_horizontal_scroll,
221 _ => &mut view.show_vertical_scroll,
222 };
223 *slot = bool_param(p, "on").unwrap_or(!*slot);
224 Ok(json!({"on": *slot}))
225 })
226}
227
228fn show_objects(s: &mut Session, p: &Json) -> Result<Json> {
230 use crate::model::ShowObjects;
231 edit(s, |cx| {
232 let on = bool_param(p, "on").unwrap_or(cx.wb.show_objects == ShowObjects::None);
233 cx.wb.show_objects = if on { ShowObjects::All } else { ShowObjects::None };
234 Ok(json!({"on": on}))
235 })
236}
237
238fn status_bar(s: &mut Session, p: &Json) -> Result<Json> {
239 let st = &mut s.prefs.status_bar;
240 for (key, flag) in [
241 ("average", &mut st.average),
242 ("count", &mut st.count),
243 ("numericalCount", &mut st.numerical_count),
244 ("minimum", &mut st.minimum),
245 ("maximum", &mut st.maximum),
246 ("sum", &mut st.sum),
247 ] {
248 if let Some(on) = bool_param(p, key) {
249 *flag = on;
250 }
251 }
252 Ok(json!({
253 "average": st.average,
254 "count": st.count,
255 "numericalCount": st.numerical_count,
256 "minimum": st.minimum,
257 "maximum": st.maximum,
258 "sum": st.sum,
259 }))
260}
261
262fn zoom(s: &mut Session, p: &Json) -> Result<Json> {
263 let z = f64_param(p, "percent").ok_or_else(|| bad("view.zoom", "missing `percent`"))?.clamp(10.0, 400.0) as u16;
264 let d = s.doc_mut()?;
265 let dirty = d.is_dirty();
266 let wb = std::sync::Arc::make_mut(&mut d.wb);
267 let si = wb.active_sheet();
268 if let Some(sh) = wb.sheet_mut(si) {
269 sh.zoom = z;
270 }
271 if !dirty {
272 d.saved = d.wb.clone();
273 }
274 Ok(json!({"zoom": z}))
275}
276
277fn zoom_selection(s: &mut Session, p: &Json) -> Result<Json> {
278 let d = s.doc()?;
279 let sh = d.wb.active().ok_or(EngineError::NoDocument)?;
280 let r = d.selection.current();
281 let w = (sh.col_left(r.end.col + 1) - sh.col_left(r.start.col)).max(1.0);
282 let h = (sh.row_top(r.end.row + 1) - sh.row_top(r.start.row)).max(1.0);
283 let vw = f64_param(p, "viewWidth").unwrap_or(1200.0);
284 let vh = f64_param(p, "viewHeight").unwrap_or(700.0);
285 let z = ((vw / w).min(vh / h) * 100.0).clamp(10.0, 400.0).floor();
286 zoom(s, &json!({"percent": z}))
287}
288
289fn orientation(s: &mut Session, p: &Json) -> Result<Json> {
290 let o = if str_param(p, "orientation") == Some("landscape") { Orientation::Landscape } else { Orientation::Portrait };
291 edit(s, |cx| {
292 let si = cx.sheet_index();
293 cx.sheet_mut(si)?.print.orientation = o;
294 Ok(Json::Null)
295 })
296}
297
298fn paper(s: &mut Session, p: &Json) -> Result<Json> {
299 let paper = str_param(p, "paper").unwrap_or("Letter").to_string();
300 edit(s, |cx| {
301 let si = cx.sheet_index();
302 cx.sheet_mut(si)?.print.paper = paper.clone();
303 Ok(Json::Null)
304 })
305}
306
307fn margins(s: &mut Session, p: &Json) -> Result<Json> {
308 let preset = match str_param(p, "preset") {
309 Some("wide") => Some([1.0, 1.0, 1.0, 1.0, 0.5, 0.5]),
310 Some("narrow") => Some([0.25, 0.25, 0.75, 0.75, 0.3, 0.3]),
311 Some("normal") => Some([0.7, 0.7, 0.75, 0.75, 0.3, 0.3]),
312 _ => None,
313 };
314 edit(s, |cx| {
315 let si = cx.sheet_index();
316 let m = &mut cx.sheet_mut(si)?.print.margins;
317 if let Some(pr) = preset {
318 *m = pr;
319 }
320 for (i, k) in ["left", "right", "top", "bottom", "header", "footer"].iter().enumerate() {
321 if let Some(v) = f64_param(p, k)
322 && let Some(slot) = m.get_mut(i)
323 {
324 *slot = v.clamp(0.0, 20.0) as f32;
325 }
326 }
327 Ok(Json::Null)
328 })
329}
330
331fn print_area(s: &mut Session, p: &Json) -> Result<Json> {
332 let clear = bool_param(p, "clear").unwrap_or(false);
333 let r = target_range(s, p)?;
334 edit(s, |cx| {
335 let si = cx.sheet_index();
336 cx.sheet_mut(si)?.print.print_area = if clear { None } else { Some(r) };
337 Ok(Json::Null)
338 })
339}
340
341fn print_titles(s: &mut Session, p: &Json) -> Result<Json> {
342 let axis = |key: &str, is_cols: bool| -> Result<Option<Option<(u32, u32)>>> {
346 match p.get(key) {
347 None => Ok(None),
348 Some(Json::Null) => Ok(Some(None)),
349 Some(Json::String(t)) if t.trim().is_empty() => Ok(Some(None)),
350 Some(Json::String(t)) => match RangeRef::parse(t) {
351 Some(r) => Ok(Some(Some(if is_cols { (r.start.col, r.end.col) } else { (r.start.row, r.end.row) }))),
352 None => Err(bad("pageLayout.printTitles", format!("`{key}` is not a range: `{t}`"))),
353 },
354 Some(_) => Err(bad("pageLayout.printTitles", format!("`{key}` must be text like \"1:2\" or \"A:A\""))),
355 }
356 };
357 let (rows, cols) = (axis("rows", false)?, axis("cols", true)?);
358 edit(s, |cx| {
359 let si = cx.sheet_index();
360 let pr = &mut cx.sheet_mut(si)?.print;
361 if let Some(t) = rows {
362 pr.title_rows = t;
363 }
364 if let Some(t) = cols {
365 pr.title_cols = t;
366 }
367 Ok(Json::Null)
368 })
369}
370
371fn scale_fit(s: &mut Session, p: &Json) -> Result<Json> {
372 edit(s, |cx| {
373 let si = cx.sheet_index();
374 let pr = &mut cx.sheet_mut(si)?.print;
375 if let Some(sc) = f64_param(p, "scale") {
376 pr.scale = sc.clamp(10.0, 400.0) as u16;
377 pr.fit_to = None;
378 }
379 if p.get("width").is_some() || p.get("height").is_some() {
380 pr.fit_to = Some((u32_param(p, "width").unwrap_or(0).min(32767) as u16, u32_param(p, "height").unwrap_or(0).min(32767) as u16));
381 }
382 Ok(Json::Null)
383 })
384}
385
386fn breaks(s: &mut Session, p: &Json) -> Result<Json> {
387 let at = cell_param(s, p, "cell")?.unwrap_or(s.doc()?.selection.active);
388 let insert = bool_param(p, "insert").unwrap_or(false);
389 let remove = bool_param(p, "remove").unwrap_or(false);
390 let reset = bool_param(p, "reset").unwrap_or(false);
391 edit(s, |cx| {
392 let si = cx.sheet_index();
393 let pr = &mut cx.sheet_mut(si)?.print;
394 if reset {
395 pr.row_breaks.clear();
396 pr.col_breaks.clear();
397 } else if insert || !remove {
398 if at.row > 0 && !pr.row_breaks.contains(&at.row) {
399 pr.row_breaks.push(at.row);
400 pr.row_breaks.sort_unstable();
401 }
402 if at.col > 0 && !pr.col_breaks.contains(&at.col) {
403 pr.col_breaks.push(at.col);
404 pr.col_breaks.sort_unstable();
405 }
406 } else {
407 pr.row_breaks.retain(|r| *r != at.row);
408 pr.col_breaks.retain(|c| *c != at.col);
409 }
410 Ok(Json::Null)
411 })
412}
413
414const MAX_HEADER_FOOTER: usize = 255;
416
417fn header_footer(s: &mut Session, p: &Json) -> Result<Json> {
418 const CMD: &str = "pageLayout.headerFooter";
419 let si = target_sheet(s, p)?;
420 let cap = |t: &str| t.chars().take(MAX_HEADER_FOOTER).collect::<String>();
421 let section = match (str_param(p, "part"), str_param(p, "section")) {
423 (None, None) => None,
424 (part, sec) => {
425 let footer = match part.unwrap_or("header") {
426 "header" => false,
427 "footer" => true,
428 other => return Err(bad(CMD, format!("`part` must be header or footer, not `{other}`"))),
429 };
430 let idx = match sec.unwrap_or("center") {
431 "left" => 0usize,
432 "center" | "centre" => 1,
433 "right" => 2,
434 other => return Err(bad(CMD, format!("`section` must be left, center or right, not `{other}`"))),
435 };
436 let page = match str_param(p, "page") {
437 None => crate::model::HfPage::Odd,
438 Some(id) => crate::model::HfPage::parse(id).ok_or_else(|| bad(CMD, format!("`page` must be odd, even or first, not `{id}`")))?,
439 };
440 Some((page, footer, idx, super::print::fields_to_codes(str_param(p, "text").unwrap_or(""))))
441 }
442 };
443 edit(s, |cx| {
444 let pr = &mut cx.sheet_mut(si)?.print;
445 if let Some(h) = str_param(p, "header") {
446 pr.header = cap(h);
447 }
448 if let Some(f) = str_param(p, "footer") {
449 pr.footer = cap(f);
450 }
451 for (key, slot) in [
452 ("firstHeader", &mut pr.first_header),
453 ("firstFooter", &mut pr.first_footer),
454 ("evenHeader", &mut pr.even_header),
455 ("evenFooter", &mut pr.even_footer),
456 ] {
457 if let Some(t) = str_param(p, key) {
458 *slot = cap(t);
459 }
460 }
461 if let Some(on) = bool_param(p, "differentFirst") {
462 pr.different_first = on;
463 }
464 if let Some(on) = bool_param(p, "differentOddEven") {
465 pr.different_odd_even = on;
466 }
467 if let Some(on) = bool_param(p, "scaleWithDoc") {
468 pr.hf_scale_with_doc = on;
469 }
470 if let Some(on) = bool_param(p, "alignWithMargins") {
471 pr.hf_align_with_margins = on;
472 }
473 if let Some((page, footer, idx, text)) = §ion {
474 let spec = pr.hf_mut(*page, *footer);
475 let mut parts = super::print::split_header_footer(spec);
476 if let Some(slot) = parts.get_mut(*idx) {
477 *slot = text.clone();
478 }
479 *spec = cap(&super::print::join_header_footer(&parts));
480 }
481 Ok(json!({
482 "header": pr.header,
483 "footer": pr.footer,
484 "differentFirst": pr.different_first,
485 "differentOddEven": pr.different_odd_even,
486 "scaleWithDoc": pr.hf_scale_with_doc,
487 "alignWithMargins": pr.hf_align_with_margins,
488 "firstHeader": pr.first_header,
489 "firstFooter": pr.first_footer,
490 "evenHeader": pr.even_header,
491 "evenFooter": pr.even_footer,
492 }))
493 })
494}
495
496pub fn themes() -> Vec<(&'static str, [u32; 12])> {
498 vec![
499 ("Craft", [0xFFFFFF, 0x000000, 0xE8E8E8, 0x0E2841, 0x156082, 0xE97132, 0x196B24, 0x0F9ED5, 0xA02B93, 0x4EA72E, 0x467886, 0x96607D]),
500 ("Slate", [0xFFFFFF, 0x1F2328, 0xEEF1F4, 0x3B4652, 0x4F6D8A, 0x8AA1B8, 0xC27C4C, 0x6F9A72, 0x9D6C9B, 0xD3B25A, 0x2F6FB0, 0x7A5C8F]),
501 ("Meadow", [0xFFFFFF, 0x16241A, 0xEEF4EA, 0x2E4A33, 0x4D8B3F, 0x9BC53D, 0xE4B33A, 0x2E8C83, 0xC4643C, 0x7B6A9E, 0x2B7A57, 0x6E7F3A]),
502 ("Ember", [0xFFFFFF, 0x2A1A14, 0xF6EEE8, 0x5A2E1E, 0xD9532B, 0xF2994A, 0xF2C94C, 0x8C3B2E, 0x4A6FA5, 0x6F8F4E, 0xB5472A, 0x8C6A5A]),
503 ("Ocean", [0xFFFFFF, 0x0B1E2D, 0xE8F1F7, 0x123A5A, 0x1F78B4, 0x33A6CC, 0x52C7B8, 0x3B5BA5, 0xF2A65A, 0x8E6CB8, 0x1A6FA8, 0x6A7FB0]),
504 ("Orchid", [0xFFFFFF, 0x24152B, 0xF3EDF6, 0x4B2C5E, 0x8E44AD, 0xC56CF0, 0xF78FB3, 0x3DC1D3, 0xF5CD79, 0x63CDDA, 0x7E3FA0, 0xB066B0]),
505 ("Graphite", [0xFFFFFF, 0x111111, 0xEDEDED, 0x333333, 0x595959, 0x7F7F7F, 0xA5A5A5, 0x3C78D8, 0xCC4125, 0x6AA84F, 0x3C78D8, 0x8E7CC3]),
506 ]
507}
508
509fn theme(s: &mut Session, p: &Json) -> Result<Json> {
510 let name = str_param(p, "name").unwrap_or("Craft").to_string();
511 let colors = p
512 .get("colors")
513 .and_then(Json::as_array)
514 .and_then(|a| {
515 let v: Vec<u32> = a.iter().filter_map(|c| c.as_str().and_then(|h| u32::from_str_radix(h.trim_start_matches('#'), 16).ok())).collect();
516 <[u32; 12]>::try_from(v).ok()
517 })
518 .or_else(|| themes().into_iter().find(|(n, _)| n.eq_ignore_ascii_case(&name)).map(|(_, c)| c))
519 .ok_or_else(|| bad("pageLayout.theme", format!("unknown theme `{name}`")))?;
520 let major = str_param(p, "majorFont").map(str::to_string);
521 let minor = str_param(p, "minorFont").map(str::to_string);
522 edit(s, |cx| {
523 cx.wb.theme.name = name.clone();
524 cx.wb.theme.colors = colors;
525 if let Some(m) = &major {
526 cx.wb.theme.major_font = m.clone();
527 }
528 if let Some(m) = &minor {
529 cx.wb.theme.minor_font = m.clone();
530 }
531 Ok(Json::Null)
532 })
533}
534
535#[cfg(test)]
536mod tests {
537 use super::*;
538
539 #[test]
540 fn zoom_100_resets_the_active_sheet_zoom() {
541 let mut s = Session::new();
542 s.new_workbook();
543 s.execute("view.zoom", json!({"percent": 150})).unwrap();
544 assert_eq!(s.execute("view.zoom100", json!({})).unwrap()["zoom"], 100);
545 assert_eq!(s.doc().unwrap().wb.active().unwrap().zoom, 100);
546 }
547
548 #[test]
549 fn formula_bar_toggles_without_a_workbook_and_defaults_to_shown() {
550 let mut s = Session::new();
551 assert!(s.prefs.formula_bar);
552 assert_eq!(s.execute("view.formulaBar", json!({})).unwrap()["on"], false);
553 assert_eq!(s.execute("view.formulaBar", json!({"on": true})).unwrap()["on"], true);
554 let old: crate::engine::Prefs = serde_json::from_str(r#"{"userName": "Ann"}"#).unwrap();
556 assert!(old.formula_bar);
557 }
558
559 #[test]
560 fn status_bar_statistics_default_to_average_count_and_sum_and_turn_on_one_at_a_time() {
561 let mut s = Session::new();
562 let all = json!({"average": true, "count": true, "numericalCount": false, "minimum": false, "maximum": false, "sum": true});
563 assert_eq!(s.execute("view.statusBar", json!({})).unwrap(), all);
564 let r = s.execute("view.statusBar", json!({"minimum": true, "sum": false, "count": "yes"})).unwrap();
565 assert_eq!(r, json!({"average": true, "count": true, "numericalCount": false, "minimum": true, "maximum": false, "sum": false}));
566 assert!(s.prefs.status_bar.minimum && !s.prefs.status_bar.sum);
567 let old: crate::engine::Prefs = serde_json::from_str(r#"{"userName": "Ann"}"#).unwrap();
569 assert_eq!(old.status_bar, crate::engine::StatusBarStats::default());
570 let part: crate::engine::Prefs = serde_json::from_str(r#"{"statusBar": {"maximum": true}}"#).unwrap();
571 assert!(part.status_bar.maximum && part.status_bar.average && part.status_bar.sum && !part.status_bar.minimum);
572 let saved = serde_json::to_value(&s.prefs).unwrap();
573 assert_eq!(saved["statusBar"]["minimum"], true);
574 assert_eq!(saved["statusBar"]["numericalCount"], false);
575 }
576}