1#![deny(missing_docs)]
2
3use std::borrow::Cow;
4use std::{collections::HashMap, fmt::Debug};
5
6use serde::{Deserialize, Serialize};
7
8use crate::{
9 cf_types::ExtendedStyle,
10 constants::{LAST_COLUMN, LAST_ROW},
11 expressions::{
12 parser::CompletionContext,
13 types::Area,
14 utils::{is_valid_column_number, is_valid_row},
15 },
16 model::{FmtSettings, Model},
17 types::{
18 Alignment, ArrayKind, BorderItem, Cell, CellType, Col, Color, HorizontalAlignment,
19 MergedCell, SheetProperties, SheetState, Style, Theme, VerticalAlignment,
20 },
21};
22
23use crate::user_model::history::{
24 ColumnData, Diff, DiffList, DiffType, History, QueueDiffs, RowData,
25};
26
27use super::border_utils::is_max_border;
28
29#[derive(Serialize, Deserialize)]
30pub enum CellArrayStructure {
31 SingleCell,
33 DynamicChild(i32, i32, i32, i32),
36 DynamicAnchor(i32, i32),
38 ArrayChild(i32, i32, i32, i32),
40 ArrayAnchor(i32, i32),
42}
43
44#[derive(Serialize, Deserialize, PartialEq)]
45pub enum BorderType {
46 All,
47 Inner,
48 Outer,
49 Top,
50 Right,
51 Bottom,
52 Left,
53 CenterH,
54 CenterV,
55 None,
56}
57
58#[derive(Serialize, Deserialize)]
60pub struct BorderArea {
61 pub(crate) item: BorderItem,
62 pub(crate) r#type: BorderType,
63}
64
65fn boolean(value: &str) -> Result<bool, String> {
66 match value {
67 "true" => Ok(true),
68 "false" => Ok(false),
69 _ => Err(format!("Invalid value for boolean: '{value}'.")),
70 }
71}
72
73fn horizontal(value: &str) -> Result<HorizontalAlignment, String> {
74 match value {
75 "center" => Ok(HorizontalAlignment::Center),
76 "centerContinuous" => Ok(HorizontalAlignment::CenterContinuous),
77 "distributed" => Ok(HorizontalAlignment::Distributed),
78 "fill" => Ok(HorizontalAlignment::Fill),
79 "general" => Ok(HorizontalAlignment::General),
80 "justify" => Ok(HorizontalAlignment::Justify),
81 "left" => Ok(HorizontalAlignment::Left),
82 "right" => Ok(HorizontalAlignment::Right),
83 _ => Err(format!(
84 "Invalid value for horizontal alignment: '{value}'."
85 )),
86 }
87}
88
89fn vertical(value: &str) -> Result<VerticalAlignment, String> {
90 match value {
91 "bottom" => Ok(VerticalAlignment::Bottom),
92 "center" => Ok(VerticalAlignment::Center),
93 "distributed" => Ok(VerticalAlignment::Distributed),
94 "justify" => Ok(VerticalAlignment::Justify),
95 "top" => Ok(VerticalAlignment::Top),
96 _ => Err(format!("Invalid value for vertical alignment: '{value}'.")),
97 }
98}
99
100fn update_style(old_value: &Style, style_path: &str, value: &str) -> Result<Style, String> {
101 let mut style = old_value.clone();
102 match style_path {
103 "font.b" => {
104 style.font.b = boolean(value)?;
105 }
106 "font.i" => {
107 style.font.i = boolean(value)?;
108 }
109 "font.u" => {
110 style.font.u = boolean(value)?;
111 }
112 "font.strike" => {
113 style.font.strike = boolean(value)?;
114 }
115 "font.color" => {
116 style.font.color = Color::from_param(value)?;
117 }
118 "font.size" => {
119 let new_size: i32 = value
120 .parse()
121 .map_err(|_| format!("Invalid value for font size: '{value}'."))?;
122 if new_size < 1 {
123 return Err(format!("Invalid value for font size: '{new_size}'."));
124 }
125 style.font.sz = new_size;
126 }
127 "font.size_delta" => {
128 let size_delta: i32 = value
130 .parse()
131 .map_err(|_| format!("Invalid value for font size: '{value}'."))?;
132 let new_size = style.font.sz + size_delta;
133 if new_size < 1 {
134 return Err(format!("Invalid value for font size: '{new_size}'."));
135 }
136 style.font.sz = new_size;
137 }
138 "fill.color" | "fill.bg_color" | "fill.fg_color" => {
139 style.fill.color = Color::from_param(value)?;
140 }
141 "num_fmt" => {
142 value.clone_into(&mut style.num_fmt);
143 }
144 "alignment" => {
145 if !value.is_empty() {
146 return Err(format!("Alignment must be empty, but found: '{value}'."));
147 }
148 style.alignment = None;
149 }
150 "alignment.horizontal" => match style.alignment {
151 Some(ref mut s) => s.horizontal = horizontal(value)?,
152 None => {
153 let alignment = Alignment {
154 horizontal: horizontal(value)?,
155 ..Default::default()
156 };
157 style.alignment = Some(alignment)
158 }
159 },
160 "alignment.vertical" => match style.alignment {
161 Some(ref mut s) => s.vertical = vertical(value)?,
162 None => {
163 let alignment = Alignment {
164 vertical: vertical(value)?,
165 ..Default::default()
166 };
167 style.alignment = Some(alignment)
168 }
169 },
170 "alignment.wrap_text" => match style.alignment {
171 Some(ref mut s) => s.wrap_text = boolean(value)?,
172 None => {
173 let alignment = Alignment {
174 wrap_text: boolean(value)?,
175 ..Default::default()
176 };
177 style.alignment = Some(alignment)
178 }
179 },
180 _ => {
181 return Err(format!("Invalid style path: '{style_path}'."));
182 }
183 }
184 Ok(style)
185}
186
187pub struct UserModel<'a> {
214 pub(crate) model: Model<'a>,
215 history: History,
216 send_queue: Vec<QueueDiffs>,
217 pause_evaluation: bool,
218}
219
220pub(crate) fn selected_sheet_after_move(selected: u32, from: u32, to: u32) -> u32 {
225 if selected == from {
226 return to;
227 }
228 let after_remove = if selected > from {
230 selected - 1
231 } else {
232 selected
233 };
234 if after_remove >= to {
235 after_remove + 1
236 } else {
237 after_remove
238 }
239}
240
241impl<'a> Debug for UserModel<'a> {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 f.debug_struct("UserModel").finish()
244 }
245}
246
247impl<'a> UserModel<'a> {
248 pub fn from_model(model: Model) -> UserModel {
250 UserModel {
251 model,
252 history: History::default(),
253 send_queue: vec![],
254 pause_evaluation: false,
255 }
256 }
257
258 pub fn new_empty(
263 name: &'a str,
264 locale_id: &'a str,
265 timezone: &'a str,
266 language_id: &'a str,
267 ) -> Result<UserModel<'a>, String> {
268 let model = Model::new_empty(name, locale_id, timezone, language_id)?;
269 Ok(UserModel {
270 model,
271 history: History::default(),
272 send_queue: vec![],
273 pause_evaluation: false,
274 })
275 }
276
277 pub fn from_bytes(s: &[u8], language_id: &'a str) -> Result<UserModel<'a>, String> {
282 let model = Model::from_bytes(s, language_id)?;
283 Ok(UserModel {
284 model,
285 history: History::default(),
286 send_queue: vec![],
287 pause_evaluation: false,
288 })
289 }
290
291 pub fn to_bytes(&self) -> Vec<u8> {
296 self.model.to_bytes()
297 }
298
299 pub fn get_model(&self) -> &Model<'_> {
301 &self.model
302 }
303
304 pub fn get_name(&self) -> String {
306 self.model.workbook.name.clone()
307 }
308
309 pub fn set_name(&mut self, name: &str) {
311 let old_value = self.model.workbook.name.clone();
312 if old_value == name {
313 return;
314 }
315 self.push_diff_list(vec![Diff::SetWorkbookName {
316 old_value,
317 new_value: name.to_string(),
318 }]);
319 self.model.workbook.name = name.to_string();
320 }
321
322 pub fn undo(&mut self) -> Result<(), String> {
327 if let Some(diff_list) = self.history.undo() {
328 self.apply_undo_diff_list(&diff_list)?;
329 self.send_queue.push(QueueDiffs {
330 r#type: DiffType::Undo,
331 list: diff_list.clone(),
332 });
333 };
334 Ok(())
335 }
336
337 pub fn redo(&mut self) -> Result<(), String> {
342 if let Some(diff_list) = self.history.redo() {
343 self.apply_diff_list(&diff_list)?;
344 self.send_queue.push(QueueDiffs {
345 r#type: DiffType::Redo,
346 list: diff_list.clone(),
347 });
348 };
349 Ok(())
350 }
351
352 pub fn can_undo(&self) -> bool {
354 !self.history.undo_stack.is_empty()
355 }
356
357 pub fn can_redo(&self) -> bool {
359 !self.history.redo_stack.is_empty()
360 }
361
362 pub fn pause_evaluation(&mut self) {
368 self.pause_evaluation = true;
369 }
370
371 pub fn resume_evaluation(&mut self) {
377 self.pause_evaluation = false;
378 }
379
380 pub fn evaluate(&mut self) {
386 self.model.evaluate()
387 }
388
389 pub fn flush_send_queue(&mut self) -> Vec<u8> {
397 let q = bitcode::encode(&self.send_queue);
399 self.send_queue = vec![];
400 q
401 }
402
403 pub fn apply_external_diffs(&mut self, diff_list_str: &[u8]) -> Result<(), String> {
410 if let Ok(queue_diffs_list) = bitcode::decode::<Vec<QueueDiffs>>(diff_list_str) {
411 for queue_diff in queue_diffs_list {
412 if matches!(queue_diff.r#type, DiffType::Redo) {
413 self.apply_diff_list(&queue_diff.list)?;
414 } else {
415 self.apply_undo_diff_list(&queue_diff.list)?;
416 }
417 }
418 } else {
419 return Err("Error parsing diff list".to_string());
420 }
421 Ok(())
422 }
423
424 pub fn set_user_input(
429 &mut self,
430 sheet: u32,
431 row: i32,
432 column: i32,
433 value: &str,
434 ) -> Result<(), String> {
435 if !is_valid_column_number(column) {
436 return Err("Invalid column".to_string());
437 }
438 if !is_valid_row(row) {
439 return Err("Invalid row".to_string());
440 }
441 let old_value = self
442 .model
443 .workbook
444 .worksheet(sheet)?
445 .cell(row, column)
446 .map(Cow::into_owned);
447 let old_value = if matches!(old_value, Some(Cell::SpillCell { .. })) {
449 None
450 } else {
451 old_value
452 };
453 let mut diff_list = vec![Diff::SetCellValue {
454 sheet,
455 row,
456 column,
457 new_value: value.to_string(),
458 old_value: Box::new(old_value),
459 }];
460 self.set_user_input_with_link_diffs(sheet, row, column, value.to_string(), &mut diff_list)?;
461
462 self.evaluate_if_not_paused();
463
464 let style = self.model.get_style_for_cell(sheet, row, column)?;
465
466 let line_count = value.split('\n').count() as f64;
467 let row_height = self.model.get_row_height(sheet, row)?;
468 let font_size = style.font.sz as f64;
470 let line_height = font_size * 1.5;
471 let cell_height = (line_count - 1.0) * line_height + 8.0 + font_size;
472 if cell_height > row_height {
473 diff_list.push(Diff::SetRowHeight {
474 sheet,
475 row,
476 new_value: cell_height,
477 old_value: row_height,
478 });
479 self.model.set_row_height(sheet, row, cell_height)?;
480 }
481
482 self.push_diff_list(diff_list);
483 Ok(())
484 }
485
486 pub(super) fn set_user_input_with_link_diffs(
492 &mut self,
493 sheet: u32,
494 row: i32,
495 column: i32,
496 value: String,
497 diff_list: &mut Vec<Diff>,
498 ) -> Result<(), String> {
499 let old_link = self.model.get_cell_link(sheet, row, column)?;
500 let old_style = self.model.get_cell_style_or_none(sheet, row, column)?;
501 self.model.set_user_input(sheet, row, column, value)?;
502 let new_link = self.model.get_cell_link(sheet, row, column)?;
503 if new_link == old_link {
504 return Ok(());
505 }
506 if old_link.is_none() {
507 let new_style = self.model.get_style_for_cell(sheet, row, column)?;
509 diff_list.push(Diff::SetCellStyle {
510 sheet,
511 row,
512 column,
513 old_value: Box::new(old_style),
514 new_value: Box::new(new_style),
515 });
516 }
517 diff_list.push(Diff::SetCellLink {
518 sheet,
519 row,
520 column,
521 old_value: Box::new(old_link),
522 new_value: Box::new(new_link),
523 });
524 Ok(())
525 }
526
527 #[inline]
532 pub fn get_cell_content(&self, sheet: u32, row: i32, column: i32) -> Result<String, String> {
533 self.model.get_localized_cell_content(sheet, row, column)
534 }
535
536 #[inline]
544 pub fn formula_completion(
545 &mut self,
546 sheet: u32,
547 row: i32,
548 column: i32,
549 formula: &str,
550 cursor: usize,
551 ) -> Result<CompletionContext, String> {
552 self.model
553 .formula_completion(sheet, row, column, formula, cursor)
554 }
555
556 #[inline]
563 pub fn cycle_reference(
564 &self,
565 value: &str,
566 start: usize,
567 end: usize,
568 ) -> Result<(String, i32, i32), String> {
569 self.model.cycle_reference(value, start, end)
570 }
571
572 #[inline]
577 pub fn get_formatted_cell_value(
578 &self,
579 sheet: u32,
580 row: i32,
581 column: i32,
582 ) -> Result<String, String> {
583 self.model.get_formatted_cell_value(sheet, row, column)
584 }
585
586 pub fn get_cell_type(&self, sheet: u32, row: i32, column: i32) -> Result<CellType, String> {
591 self.model.get_cell_type(sheet, row, column)
592 }
593
594 pub fn new_sheet(&mut self) -> Result<(), String> {
599 let (name, index) = self.model.new_sheet();
600 self.set_selected_sheet(index)?;
601 self.push_diff_list(vec![Diff::NewSheet { index, name }]);
602 Ok(())
603 }
604
605 pub fn duplicate_sheet(&mut self, sheet: u32) -> Result<(), String> {
611 let (_name, new_index) = self.model.duplicate_sheet(sheet)?;
612 self.set_selected_sheet(new_index)?;
613 self.push_diff_list(vec![Diff::DuplicateSheet {
614 source_index: sheet,
615 new_index,
616 }]);
617 Ok(())
618 }
619
620 pub fn delete_sheet(&mut self, sheet: u32) -> Result<(), String> {
625 let worksheet = self.model.workbook.worksheet(sheet)?;
626
627 self.push_diff_list(vec![Diff::DeleteSheet {
628 sheet,
629 old_data: Box::new(worksheet.clone()),
630 }]);
631
632 let sheet_count = self.model.workbook.worksheets.len() as u32;
633 if sheet == sheet_count - 1 && sheet_count > 1 {
635 if let Some(view) = self.model.workbook.views.get_mut(&self.model.view_id) {
636 view.sheet = sheet_count - 2;
637 };
638 }
639
640 self.model.delete_sheet(sheet)?;
641 Ok(())
642 }
643
644 pub fn rename_sheet(&mut self, sheet: u32, new_name: &str) -> Result<(), String> {
649 let old_value = self.model.workbook.worksheet(sheet)?.name.clone();
650 if old_value == new_name {
651 return Ok(());
652 }
653 self.model.rename_sheet_by_index(sheet, new_name)?;
654 self.push_diff_list(vec![Diff::RenameSheet {
655 index: sheet,
656 old_value,
657 new_value: new_name.to_string(),
658 }]);
659 Ok(())
660 }
661
662 pub fn move_sheet(&mut self, sheet_index: u32, new_index: u32) -> Result<(), String> {
677 let sheet_count = self.model.workbook.worksheets.len() as u32;
678 if sheet_index >= sheet_count {
679 return Err(format!("Invalid sheet index {sheet_index}"));
680 }
681 if new_index >= sheet_count {
682 return Err(format!("Invalid target index {new_index}"));
683 }
684 if sheet_index == new_index {
685 return Ok(());
686 }
687 let selected = self.get_selected_sheet();
688 self.model.move_sheet(sheet_index, new_index)?;
689 self.set_selected_sheet(selected_sheet_after_move(selected, sheet_index, new_index))?;
690 self.push_diff_list(vec![Diff::MoveSheet {
691 sheet_index,
692 new_index,
693 }]);
694 Ok(())
695 }
696
697 pub fn hide_sheet(&mut self, sheet: u32) -> Result<(), String> {
703 let sheet_count = self.model.workbook.worksheets.len() as u32;
704 for index in 1..sheet_count {
705 let sheet_index = (sheet + index) % sheet_count;
706 if self.model.workbook.worksheet(sheet_index)?.state == SheetState::Visible {
707 if let Some(view) = self.model.workbook.views.get_mut(&self.model.view_id) {
708 view.sheet = sheet_index;
709 };
710 break;
711 }
712 }
713 let old_value = self.model.workbook.worksheet(sheet)?.state.clone();
714 self.push_diff_list(vec![Diff::SetSheetState {
715 index: sheet,
716 new_value: SheetState::Hidden,
717 old_value,
718 }]);
719 self.model.set_sheet_state(sheet, SheetState::Hidden)?;
720 Ok(())
721 }
722
723 pub fn unhide_sheet(&mut self, sheet: u32) -> Result<(), String> {
729 let old_value = self.model.workbook.worksheet(sheet)?.state.clone();
730 self.push_diff_list(vec![Diff::SetSheetState {
731 index: sheet,
732 new_value: SheetState::Visible,
733 old_value,
734 }]);
735 self.model.set_sheet_state(sheet, SheetState::Visible)?;
736 Ok(())
737 }
738
739 pub fn set_sheet_color(&mut self, sheet: u32, color: &Color) -> Result<(), String> {
747 let old_value = self.model.workbook.worksheet(sheet)?.color.clone();
748 self.model.set_sheet_color(sheet, color)?;
749 self.push_diff_list(vec![Diff::SetSheetColor {
750 index: sheet,
751 old_value,
752 new_value: color.clone(),
753 }]);
754 Ok(())
755 }
756
757 pub fn range_clear_all(&mut self, range: &Area) -> Result<(), String> {
762 let sheet = range.sheet;
763 let mut old_value = Vec::new();
765 let mut old_style = Vec::new();
766
767 for row in range.row..range.row + range.height {
768 let mut data_row = Vec::new();
769 let mut style_row = Vec::new();
770 for column in range.column..range.column + range.width {
771 let old_value = self
772 .model
773 .workbook
774 .worksheet(sheet)?
775 .cell(row, column)
776 .map(Cow::into_owned);
777 data_row.push(old_value);
778 let old_style = self.model.get_style_for_cell(sheet, row, column)?;
779 style_row.push(old_style);
780 }
781 old_value.push(data_row);
782 old_style.push(style_row);
783 }
784 let link_diffs = self.range_link_diffs(range)?;
786 self.model.range_clear_all(range)?;
787 let mut diff_list = vec![Diff::RangeClearAll {
788 sheet,
789 row: range.row,
790 column: range.column,
791 width: range.width,
792 height: range.height,
793 old_value,
794 old_style,
795 }];
796 diff_list.extend(link_diffs);
797
798 self.push_diff_list(diff_list);
799 self.evaluate_if_not_paused();
800 Ok(())
801 }
802
803 pub fn range_clear_contents(&mut self, range: &Area) -> Result<(), String> {
808 let sheet = range.sheet;
809 let mut old_value = Vec::new();
811 for row in range.row..range.row + range.height {
812 let mut data_row = Vec::new();
813 for column in range.column..range.column + range.width {
814 let old_value = self
815 .model
816 .workbook
817 .worksheet(sheet)?
818 .cell(row, column)
819 .map(Cow::into_owned);
820 data_row.push(old_value);
821 }
822 old_value.push(data_row);
823 }
824 let link_diffs = self.range_link_diffs(range)?;
826 self.model.range_clear_contents(range)?;
827 let mut diff_list = vec![Diff::RangeClearContents {
828 sheet,
829 row: range.row,
830 column: range.column,
831 width: range.width,
832 height: range.height,
833 old_value,
834 }];
835 diff_list.extend(link_diffs);
836 self.push_diff_list(diff_list);
837 self.evaluate_if_not_paused();
838 Ok(())
839 }
840
841 pub(super) fn range_link_diffs(&self, range: &Area) -> Result<Vec<Diff>, String> {
844 let mut diffs = Vec::new();
845 for (&(row, column), link) in &self.model.workbook.worksheet(range.sheet)?.links {
846 if row >= range.row
847 && row < range.row + range.height
848 && column >= range.column
849 && column < range.column + range.width
850 {
851 diffs.push(Diff::SetCellLink {
852 sheet: range.sheet,
853 row,
854 column,
855 old_value: Box::new(Some(link.clone())),
856 new_value: Box::new(None),
857 });
858 }
859 }
860 Ok(diffs)
861 }
862
863 fn clear_column_formatting(
864 &mut self,
865 sheet: u32,
866 column: i32,
867 diff_list: &mut Vec<Diff>,
868 ) -> Result<(), String> {
869 let old_value = self.model.get_column_style(sheet, column)?;
870 self.model.delete_column_style(sheet, column)?;
871 diff_list.push(Diff::DeleteColumnStyle {
872 sheet,
873 column,
874 old_value: Box::new(old_value),
875 });
876
877 let data_rows: Vec<i32> = self.model.workbook.worksheet(sheet)?.sheet_data.rows();
878 let styled_rows = &self.model.workbook.worksheet(sheet)?.rows.clone();
879
880 for row in data_rows {
882 if let Some(old_style) = self.model.get_cell_style_or_none(sheet, row, column)? {
883 self.model
885 .workbook
886 .worksheet_mut(sheet)?
887 .set_cell_style(row, column, 0)?;
888 diff_list.push(Diff::CellClearFormatting {
889 sheet,
890 row,
891 column,
892 old_style: Box::new(Some(old_style)),
893 });
894 } else {
895 let old_style = self.model.get_style_for_cell(sheet, row, column)?;
896 if old_style != Style::default() {
897 self.model
898 .workbook
899 .worksheet_mut(sheet)?
900 .set_cell_style(row, column, 0)?;
901 diff_list.push(Diff::CellClearFormatting {
902 sheet,
903 row,
904 column,
905 old_style: Box::new(None),
906 });
907 }
908 }
909 }
910 for row in styled_rows {
912 if let Some(old_style) = self.model.get_cell_style_or_none(sheet, row.r, column)? {
913 self.model
915 .workbook
916 .worksheet_mut(sheet)?
917 .set_cell_style(row.r, column, 0)?;
918 diff_list.push(Diff::CellClearFormatting {
919 sheet,
920 row: row.r,
921 column,
922 old_style: Box::new(Some(old_style)),
923 });
924 } else {
925 let old_style = self.model.get_style_for_cell(sheet, row.r, column)?;
926 if old_style != Style::default() {
927 self.model
928 .workbook
929 .worksheet_mut(sheet)?
930 .set_cell_style(row.r, column, 0)?;
931 diff_list.push(Diff::CellClearFormatting {
932 sheet,
933 row: row.r,
934 column,
935 old_style: Box::new(None),
936 });
937 }
938 }
939 }
940 Ok(())
941 }
942
943 fn clear_row_formatting(
944 &mut self,
945 sheet: u32,
946 row: i32,
947 diff_list: &mut Vec<Diff>,
948 ) -> Result<(), String> {
949 let old_value = self.model.get_row_style(sheet, row)?;
950 self.model.delete_row_style(sheet, row)?;
951 diff_list.push(Diff::DeleteRowStyle {
952 sheet,
953 row,
954 old_value: Box::new(old_value),
955 });
956
957 let columns: Vec<i32> = self
959 .model
960 .workbook
961 .worksheet(sheet)?
962 .sheet_data
963 .columns_in_row(row);
964 for column in columns {
965 if let Some(old_style) = self.model.get_cell_style_or_none(sheet, row, column)? {
966 self.model
968 .workbook
969 .worksheet_mut(sheet)?
970 .set_cell_style(row, column, 0)?;
971 diff_list.push(Diff::CellClearFormatting {
972 sheet,
973 row,
974 column,
975 old_style: Box::new(Some(old_style)),
976 });
977 } else {
978 let old_style = self.model.get_style_for_cell(sheet, row, column)?;
979 if old_style != Style::default() {
980 self.model
981 .workbook
982 .worksheet_mut(sheet)?
983 .set_cell_style(row, column, 0)?;
984 diff_list.push(Diff::CellClearFormatting {
985 sheet,
986 row,
987 column,
988 old_style: Box::new(None),
989 });
990 }
991 }
992 }
993 Ok(())
994 }
995
996 pub fn range_clear_formatting(&mut self, range: &Area) -> Result<(), String> {
1002 let sheet = range.sheet;
1003 let mut diff_list = Vec::new();
1004 if range.row == 1 && range.height == LAST_ROW {
1005 for column in range.column..range.column + range.width {
1006 self.clear_column_formatting(sheet, column, &mut diff_list)?;
1007 }
1008 self.push_diff_list(diff_list);
1009 return Ok(());
1010 }
1011 if range.column == 1 && range.width == LAST_COLUMN {
1012 for row in range.row..range.row + range.height {
1013 self.clear_row_formatting(sheet, row, &mut diff_list)?;
1014 }
1015 self.push_diff_list(diff_list);
1016 return Ok(());
1017 }
1018 for row in range.row..range.row + range.height {
1019 for column in range.column..range.column + range.width {
1020 if let Some(old_style) = self.model.get_cell_style_or_none(sheet, row, column)? {
1021 self.model
1023 .workbook
1024 .worksheet_mut(sheet)?
1025 .set_cell_style(row, column, 0)?;
1026 diff_list.push(Diff::CellClearFormatting {
1027 sheet,
1028 row,
1029 column,
1030 old_style: Box::new(Some(old_style)),
1031 });
1032 } else {
1033 let old_style = self.model.get_style_for_cell(sheet, row, column)?;
1034 if old_style != Style::default() {
1035 self.model
1036 .workbook
1037 .worksheet_mut(sheet)?
1038 .set_cell_style(row, column, 0)?;
1039 diff_list.push(Diff::CellClearFormatting {
1040 sheet,
1041 row,
1042 column,
1043 old_style: Box::new(None),
1044 });
1045 }
1046 }
1047 }
1048 }
1049 self.push_diff_list(diff_list);
1050 Ok(())
1051 }
1052
1053 pub fn insert_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
1067 let old_merged_cells = self.model.get_merged_cells(sheet)?.to_vec();
1068 self.model.insert_rows(sheet, row, row_count)?;
1069
1070 let mut diff_list = self.merged_cells_snapshot_diffs(sheet, old_merged_cells)?;
1071 diff_list.push(Diff::InsertRows {
1072 sheet,
1073 row,
1074 count: row_count,
1075 });
1076 self.push_diff_list(diff_list);
1077 self.evaluate_if_not_paused();
1078 Ok(())
1079 }
1080
1081 pub fn insert_columns(
1094 &mut self,
1095 sheet: u32,
1096 column: i32,
1097 column_count: i32,
1098 ) -> Result<(), String> {
1099 let old_merged_cells = self.model.get_merged_cells(sheet)?.to_vec();
1100 self.model.insert_columns(sheet, column, column_count)?;
1101
1102 let mut diff_list = self.merged_cells_snapshot_diffs(sheet, old_merged_cells)?;
1103 diff_list.push(Diff::InsertColumns {
1104 sheet,
1105 column,
1106 count: column_count,
1107 });
1108 self.push_diff_list(diff_list);
1109 self.evaluate_if_not_paused();
1110 Ok(())
1111 }
1112
1113 pub fn delete_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
1121 let worksheet = self.model.workbook.worksheet(sheet)?;
1122 let mut old_data = Vec::new();
1123 for r in row..row + row_count {
1125 let mut row_data = None;
1126 for rd in &worksheet.rows {
1127 if rd.r == r {
1128 row_data = Some(rd.clone());
1129 break;
1130 }
1131 }
1132 let data = worksheet
1135 .sheet_data
1136 .cells_in_row(r)
1137 .into_iter()
1138 .map(|(k, v)| {
1139 let cell = if let Cell::SpillCell { s, .. } = &*v {
1140 Cell::EmptyCell { s: *s }
1141 } else {
1142 v.into_owned()
1143 };
1144 (k, cell)
1145 })
1146 .collect();
1147 old_data.push(RowData {
1148 row: row_data,
1149 data,
1150 });
1151 }
1152
1153 let link_diffs = self.range_link_diffs(&Area {
1157 sheet,
1158 row,
1159 column: 1,
1160 width: LAST_COLUMN,
1161 height: row_count,
1162 })?;
1163
1164 let old_merged_cells = self.model.get_merged_cells(sheet)?.to_vec();
1165 self.model.delete_rows(sheet, row, row_count)?;
1166
1167 let mut diff_list = self.merged_cells_snapshot_diffs(sheet, old_merged_cells)?;
1168 diff_list.extend(link_diffs);
1169 diff_list.push(Diff::DeleteRows {
1170 sheet,
1171 row,
1172 count: row_count,
1173 old_data,
1174 });
1175 self.push_diff_list(diff_list);
1176 self.evaluate_if_not_paused();
1177 Ok(())
1178 }
1179
1180 pub fn delete_columns(
1188 &mut self,
1189 sheet: u32,
1190 column: i32,
1191 column_count: i32,
1192 ) -> Result<(), String> {
1193 let worksheet = self.model.workbook.worksheet(sheet)?;
1194 let mut old_data = Vec::new();
1195 for c in column..column + column_count {
1197 let mut column_data = None;
1198 for col in &worksheet.cols {
1199 if c >= col.min && c <= col.max {
1200 column_data = Some(Col {
1201 min: c,
1202 max: c,
1203 width: col.width,
1204 custom_width: col.custom_width,
1205 style: col.style,
1206 hidden: col.hidden,
1207 });
1208 break;
1209 }
1210 }
1211
1212 let mut data = HashMap::new();
1215 for row_idx in worksheet.sheet_data.rows() {
1216 if let Some(cell) = worksheet.cell(row_idx, c) {
1217 let saved = if let Cell::SpillCell { s, .. } = &*cell {
1218 Cell::EmptyCell { s: *s }
1219 } else {
1220 cell.into_owned()
1221 };
1222 data.insert(row_idx, saved);
1223 }
1224 }
1225
1226 old_data.push(ColumnData {
1227 column: column_data,
1228 data,
1229 });
1230 }
1231
1232 let link_diffs = self.range_link_diffs(&Area {
1237 sheet,
1238 row: 1,
1239 column,
1240 width: column_count,
1241 height: LAST_ROW,
1242 })?;
1243
1244 let old_merged_cells = self.model.get_merged_cells(sheet)?.to_vec();
1245 self.model.delete_columns(sheet, column, column_count)?;
1246
1247 let mut diff_list = self.merged_cells_snapshot_diffs(sheet, old_merged_cells)?;
1248 diff_list.extend(link_diffs);
1249 diff_list.push(Diff::DeleteColumns {
1250 sheet,
1251 column,
1252 count: column_count,
1253 old_data,
1254 });
1255 self.push_diff_list(diff_list);
1256 self.evaluate_if_not_paused();
1257 Ok(())
1258 }
1259
1260 pub fn move_columns_action(
1262 &mut self,
1263 sheet: u32,
1264 column: i32,
1265 column_count: i32,
1266 delta: i32,
1267 ) -> Result<(), String> {
1268 if delta == 0 || column_count <= 0 {
1269 return Ok(());
1270 }
1271 let mut new_delta = delta;
1273 let worksheet = self.model.workbook.worksheet(sheet)?;
1274 if delta > 0 {
1275 for col in column + column_count..=column + column_count + delta {
1276 if worksheet.is_column_hidden(col)? {
1277 new_delta += 1;
1278 }
1279 }
1280 } else {
1281 for col in column + delta..column {
1282 if worksheet.is_column_hidden(col)? {
1283 new_delta -= 1;
1284 }
1285 }
1286 }
1287
1288 let old_merged_cells = self.model.get_merged_cells(sheet)?.to_vec();
1289 self.model
1290 .move_columns_action(sheet, column, column_count, new_delta)?;
1291
1292 let mut diff_list = self.merged_cells_snapshot_diffs(sheet, old_merged_cells)?;
1293 diff_list.push(Diff::MoveColumns {
1294 sheet,
1295 column,
1296 column_count,
1297 delta: new_delta,
1298 });
1299 self.push_diff_list(diff_list);
1300 self.evaluate_if_not_paused();
1301 Ok(())
1302 }
1303
1304 pub fn move_rows_action(
1306 &mut self,
1307 sheet: u32,
1308 row: i32,
1309 row_count: i32,
1310 delta: i32,
1311 ) -> Result<(), String> {
1312 if delta == 0 || row_count <= 0 {
1313 return Ok(());
1314 }
1315 let mut new_delta = delta;
1316 let worksheet = self.model.workbook.worksheet(sheet)?;
1317 if delta > 0 {
1318 for r in row + row_count..=row + row_count + delta {
1319 if worksheet.is_row_hidden(r)? {
1320 new_delta += 1;
1321 }
1322 }
1323 } else {
1324 for r in row + delta..row {
1325 if worksheet.is_row_hidden(r)? {
1326 new_delta -= 1;
1327 }
1328 }
1329 }
1330
1331 let old_merged_cells = self.model.get_merged_cells(sheet)?.to_vec();
1332 self.model
1333 .move_rows_action(sheet, row, row_count, new_delta)?;
1334
1335 let mut diff_list = self.merged_cells_snapshot_diffs(sheet, old_merged_cells)?;
1336 diff_list.push(Diff::MoveRows {
1337 sheet,
1338 row,
1339 row_count,
1340 delta: new_delta,
1341 });
1342 self.push_diff_list(diff_list);
1343 self.evaluate_if_not_paused();
1344 Ok(())
1345 }
1346
1347 pub fn set_columns_width(
1352 &mut self,
1353 sheet: u32,
1354 column_start: i32,
1355 column_end: i32,
1356 width: f64,
1357 ) -> Result<(), String> {
1358 let mut diff_list = Vec::new();
1359 for column in column_start..=column_end {
1360 let old_value = self.model.get_column_width(sheet, column)?;
1361 diff_list.push(Diff::SetColumnWidth {
1362 sheet,
1363 column,
1364 new_value: width,
1365 old_value,
1366 });
1367 self.model.set_column_width(sheet, column, width)?;
1368 }
1369 self.push_diff_list(diff_list);
1370 Ok(())
1371 }
1372
1373 pub fn set_columns_hidden(
1378 &mut self,
1379 sheet: u32,
1380 column_start: i32,
1381 column_end: i32,
1382 hidden: bool,
1383 ) -> Result<(), String> {
1384 let mut diff_list = Vec::new();
1385 for column in column_start..=column_end {
1386 let old_value = self
1387 .model
1388 .workbook
1389 .worksheet(sheet)?
1390 .is_column_hidden(column)?;
1391 diff_list.push(Diff::SetColumnHidden {
1392 sheet,
1393 column,
1394 new_value: hidden,
1395 old_value,
1396 });
1397 self.model.set_column_hidden(sheet, column, hidden)?;
1398 }
1399 if hidden {
1401 if let Some(view) = self.model.workbook.views.get_mut(&self.model.view_id) {
1402 if view.sheet == sheet {
1403 let mut column = column_end + 1;
1405 while self
1406 .model
1407 .workbook
1408 .worksheet(sheet)?
1409 .is_column_hidden(column)?
1410 {
1411 column += 1;
1412 if column > LAST_COLUMN {
1413 break;
1414 }
1415 }
1416 if column > LAST_COLUMN {
1417 column = column_start - 1;
1419 while self
1420 .model
1421 .workbook
1422 .worksheet(sheet)?
1423 .is_column_hidden(column)?
1424 {
1425 column -= 1;
1426 if column <= 0 {
1427 column = 1;
1429 break;
1430 }
1431 }
1432 }
1433 self.set_selected_cell(1, column)?;
1434 self.set_selected_range(1, column, LAST_ROW, column)?;
1435 }
1436 };
1437 }
1438 self.push_diff_list(diff_list);
1439 Ok(())
1440 }
1441
1442 pub fn set_rows_hidden(
1446 &mut self,
1447 sheet: u32,
1448 row_start: i32,
1449 row_end: i32,
1450 hidden: bool,
1451 ) -> Result<(), String> {
1452 let mut diff_list = Vec::new();
1453 for row in row_start..=row_end {
1454 let old_value = self.model.workbook.worksheet(sheet)?.is_row_hidden(row)?;
1455 diff_list.push(Diff::SetRowHidden {
1456 sheet,
1457 row,
1458 new_value: hidden,
1459 old_value,
1460 });
1461 self.model.set_row_hidden(sheet, row, hidden)?;
1462 }
1463 if hidden {
1465 if let Some(view) = self.model.workbook.views.get_mut(&self.model.view_id) {
1466 if view.sheet == sheet {
1467 let mut row = row_end + 1;
1469 while self.model.workbook.worksheet(sheet)?.is_row_hidden(row)? {
1470 row += 1;
1471 if row > LAST_ROW {
1472 break;
1473 }
1474 }
1475 if row > LAST_ROW {
1476 row = row_start - 1;
1478 while self.model.workbook.worksheet(sheet)?.is_row_hidden(row)? {
1479 row -= 1;
1480 if row <= 0 {
1481 row = 1;
1483 break;
1484 }
1485 }
1486 }
1487 self.set_selected_cell(row, 1)?;
1488 self.set_selected_range(row, 1, row, LAST_COLUMN)?;
1489 }
1490 };
1491 }
1492 self.push_diff_list(diff_list);
1493 Ok(())
1494 }
1495
1496 pub fn set_rows_height(
1501 &mut self,
1502 sheet: u32,
1503 row_start: i32,
1504 row_end: i32,
1505 height: f64,
1506 ) -> Result<(), String> {
1507 let mut diff_list = Vec::new();
1508 for row in row_start..=row_end {
1509 let old_value = self.model.get_row_height(sheet, row)?;
1510 diff_list.push(Diff::SetRowHeight {
1511 sheet,
1512 row,
1513 new_value: height,
1514 old_value,
1515 });
1516 self.model.set_row_height(sheet, row, height)?;
1517 }
1518 self.push_diff_list(diff_list);
1519 Ok(())
1520 }
1521
1522 #[inline]
1527 pub fn get_row_height(&self, sheet: u32, row: i32) -> Result<f64, String> {
1528 self.model.get_row_height(sheet, row)
1529 }
1530
1531 #[inline]
1536 pub fn get_column_width(&self, sheet: u32, column: i32) -> Result<f64, String> {
1537 self.model.get_column_width(sheet, column)
1538 }
1539
1540 #[inline]
1545 pub fn get_frozen_rows_count(&self, sheet: u32) -> Result<i32, String> {
1546 self.model.get_frozen_rows_count(sheet)
1547 }
1548
1549 #[inline]
1554 pub fn get_frozen_columns_count(&self, sheet: u32) -> Result<i32, String> {
1555 self.model.get_frozen_columns_count(sheet)
1556 }
1557
1558 pub fn set_frozen_rows_count(&mut self, sheet: u32, frozen_rows: i32) -> Result<(), String> {
1563 let old_value = self.model.get_frozen_rows_count(sheet)?;
1564 self.push_diff_list(vec![Diff::SetFrozenRowsCount {
1565 sheet,
1566 new_value: frozen_rows,
1567 old_value,
1568 }]);
1569 self.model.set_frozen_rows(sheet, frozen_rows)
1570 }
1571
1572 pub fn set_frozen_columns_count(
1577 &mut self,
1578 sheet: u32,
1579 frozen_columns: i32,
1580 ) -> Result<(), String> {
1581 let old_value = self.model.get_frozen_columns_count(sheet)?;
1582 self.push_diff_list(vec![Diff::SetFrozenColumnsCount {
1583 sheet,
1584 new_value: frozen_columns,
1585 old_value,
1586 }]);
1587 self.model.set_frozen_columns(sheet, frozen_columns)
1588 }
1589
1590 pub fn on_paste_styles(&mut self, styles: &[Vec<Style>]) -> Result<(), String> {
1592 let styles_height = styles.len() as i32;
1593 let styles_width = styles[0].len() as i32;
1594 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
1595 view.sheet
1596 } else {
1597 return Ok(());
1598 };
1599 let range = if let Ok(worksheet) = self.model.workbook.worksheet(sheet) {
1600 if let Some(view) = worksheet.views.get(&self.model.view_id) {
1601 view.range
1602 } else {
1603 return Ok(());
1604 }
1605 } else {
1606 return Ok(());
1607 };
1608
1609 let [row_start, column_start, row_end, column_end] = range;
1611 let last_row = row_end.max(row_start + styles_height - 1);
1612 let last_column = column_end.max(column_start + styles_width - 1);
1613
1614 let mut diff_list = Vec::new();
1615 for row in row_start..=last_row {
1616 for column in column_start..=last_column {
1617 let row_index = ((row - row_start) % styles_height) as usize;
1618 let column_index = ((column - column_start) % styles_width) as usize;
1619 let style = &styles[row_index][column_index];
1620 let old_value = self.model.get_cell_style_or_none(sheet, row, column)?;
1621 self.model.set_cell_style(sheet, row, column, style)?;
1622 diff_list.push(Diff::SetCellStyle {
1623 sheet,
1624 row,
1625 column,
1626 old_value: Box::new(old_value),
1627 new_value: Box::new(style.clone()),
1628 });
1629 }
1630 }
1631 self.push_diff_list(diff_list);
1632
1633 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
1635 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
1636 view.range = [row_start, column_start, last_row, last_column];
1637 view.focus_row = last_row;
1638 view.focus_column = last_column;
1639 }
1640 }
1641 Ok(())
1642 }
1643
1644 fn update_single_cell_style(
1646 &mut self,
1647 sheet: u32,
1648 row: i32,
1649 column: i32,
1650 style_path: &str,
1651 value: &str,
1652 diff_list: &mut Vec<Diff>,
1653 ) -> Result<(), String> {
1654 let old_value = self.model.get_cell_style_or_none(sheet, row, column)?;
1656
1657 let old_style = self.model.get_style_for_cell(sheet, row, column)?;
1660 let new_style = update_style(&old_style, style_path, value)?;
1661 self.model.set_cell_style(sheet, row, column, &new_style)?;
1662 diff_list.push(Diff::SetCellStyle {
1663 sheet,
1664 row,
1665 column,
1666 old_value: Box::new(old_value),
1667 new_value: Box::new(new_style),
1668 });
1669 Ok(())
1670 }
1671
1672 pub fn update_range_style(
1676 &mut self,
1677 range: &Area,
1678 style_path: &str,
1679 value: &str,
1680 ) -> Result<(), String> {
1681 let sheet = range.sheet;
1682 let mut diff_list = Vec::new();
1683 if range.row == 1 && range.height == LAST_ROW {
1684 let styled_rows = &self.model.workbook.worksheet(sheet)?.rows.clone();
1686 let data_rows: Vec<i32> = self.model.workbook.worksheet(sheet)?.sheet_data.rows();
1689 for column in range.column..range.column + range.width {
1690 let old_style = self.model.get_column_style(sheet, column)?;
1692 let style = match old_style.as_ref() {
1693 Some(s) => s,
1694 None => &Style::default(),
1695 };
1696 let style = update_style(style, style_path, value)?;
1697 self.model.set_column_style(sheet, column, &style)?;
1698 diff_list.push(Diff::SetColumnStyle {
1699 sheet,
1700 column,
1701 old_value: Box::new(old_style),
1702 new_value: Box::new(style),
1703 });
1704
1705 for row_s in styled_rows.iter() {
1707 let row = row_s.r;
1708 self.update_single_cell_style(
1709 sheet,
1710 row,
1711 column,
1712 style_path,
1713 value,
1714 &mut diff_list,
1715 )?;
1716 }
1717
1718 for &row in &data_rows {
1721 if self
1722 .model
1723 .workbook
1724 .worksheet(sheet)?
1725 .cell(row, column)
1726 .is_some()
1727 {
1728 self.update_single_cell_style(
1730 sheet,
1731 row,
1732 column,
1733 style_path,
1734 value,
1735 &mut diff_list,
1736 )?;
1737 }
1738 }
1739 }
1740 } else if range.column == 1 && range.width == LAST_COLUMN {
1741 let styled_columns = &self.model.workbook.worksheet(sheet)?.cols.clone();
1743 for row in range.row..range.row + range.height {
1744 let columns: Vec<i32> = self
1746 .model
1747 .workbook
1748 .worksheet(sheet)?
1749 .sheet_data
1750 .columns_in_row(row);
1751 for column in columns {
1752 self.update_single_cell_style(
1753 sheet,
1754 row,
1755 column,
1756 style_path,
1757 value,
1758 &mut diff_list,
1759 )?;
1760 }
1761
1762 for col in styled_columns.iter() {
1764 for column in col.min..col.max + 1 {
1765 self.update_single_cell_style(
1766 sheet,
1767 row,
1768 column,
1769 style_path,
1770 value,
1771 &mut diff_list,
1772 )?;
1773 }
1774 }
1775
1776 let old_style = self.model.get_row_style(sheet, row)?;
1778 let style = match old_style.as_ref() {
1779 Some(s) => s,
1780 None => &Style::default(),
1781 };
1782 let style = update_style(style, style_path, value)?;
1783 self.model.set_row_style(sheet, row, &style)?;
1784 diff_list.push(Diff::SetRowStyle {
1785 sheet,
1786 row,
1787 old_value: Box::new(old_style),
1788 new_value: Box::new(style),
1789 });
1790 }
1791 } else {
1792 for row in range.row..range.row + range.height {
1793 for column in range.column..range.column + range.width {
1794 self.update_single_cell_style(
1795 sheet,
1796 row,
1797 column,
1798 style_path,
1799 value,
1800 &mut diff_list,
1801 )?;
1802 }
1803 }
1804 }
1805 self.push_diff_list(diff_list);
1806 Ok(())
1807 }
1808
1809 pub fn get_cell_style(&self, sheet: u32, row: i32, column: i32) -> Result<Style, String> {
1818 let mut style = self.model.get_style_for_cell(sheet, row, column)?;
1819
1820 let border_top = if row > 1 {
1822 self.model
1823 .get_style_for_cell(sheet, row - 1, column)?
1824 .border
1825 .bottom
1826 } else {
1827 None
1828 };
1829
1830 let border_right = if column < LAST_COLUMN {
1831 self.model
1832 .get_style_for_cell(sheet, row, column + 1)?
1833 .border
1834 .left
1835 } else {
1836 None
1837 };
1838
1839 let border_bottom = if row < LAST_ROW {
1840 self.model
1841 .get_style_for_cell(sheet, row + 1, column)?
1842 .border
1843 .top
1844 } else {
1845 None
1846 };
1847
1848 let border_left = if column > 1 {
1849 self.model
1850 .get_style_for_cell(sheet, row, column - 1)?
1851 .border
1852 .right
1853 } else {
1854 None
1855 };
1856
1857 if is_max_border(style.border.top.as_ref(), border_top.as_ref()) {
1858 style.border.top = border_top;
1859 }
1860
1861 if is_max_border(style.border.right.as_ref(), border_right.as_ref()) {
1862 style.border.right = border_right;
1863 }
1864
1865 if is_max_border(style.border.bottom.as_ref(), border_bottom.as_ref()) {
1866 style.border.bottom = border_bottom;
1867 }
1868
1869 if is_max_border(style.border.left.as_ref(), border_left.as_ref()) {
1870 style.border.left = border_left;
1871 }
1872
1873 Ok(style)
1874 }
1875
1876 pub fn get_extended_cell_style(
1881 &self,
1882 sheet: u32,
1883 row: i32,
1884 column: i32,
1885 ) -> Result<ExtendedStyle, String> {
1886 let mut extended = self.model.get_extended_style_for_cell(sheet, row, column)?;
1887
1888 let border_top = if row > 1 {
1889 self.model
1890 .get_style_for_cell(sheet, row - 1, column)?
1891 .border
1892 .bottom
1893 } else {
1894 None
1895 };
1896
1897 let border_right = if column < LAST_COLUMN {
1898 self.model
1899 .get_style_for_cell(sheet, row, column + 1)?
1900 .border
1901 .left
1902 } else {
1903 None
1904 };
1905
1906 let border_bottom = if row < LAST_ROW {
1907 self.model
1908 .get_style_for_cell(sheet, row + 1, column)?
1909 .border
1910 .top
1911 } else {
1912 None
1913 };
1914
1915 let border_left = if column > 1 {
1916 self.model
1917 .get_style_for_cell(sheet, row, column - 1)?
1918 .border
1919 .right
1920 } else {
1921 None
1922 };
1923
1924 if is_max_border(extended.style.border.top.as_ref(), border_top.as_ref()) {
1925 extended.style.border.top = border_top;
1926 }
1927
1928 if is_max_border(extended.style.border.right.as_ref(), border_right.as_ref()) {
1929 extended.style.border.right = border_right;
1930 }
1931
1932 if is_max_border(
1933 extended.style.border.bottom.as_ref(),
1934 border_bottom.as_ref(),
1935 ) {
1936 extended.style.border.bottom = border_bottom;
1937 }
1938
1939 if is_max_border(extended.style.border.left.as_ref(), border_left.as_ref()) {
1940 extended.style.border.left = border_left;
1941 }
1942
1943 Ok(extended)
1944 }
1945
1946 #[inline]
1951 pub fn get_worksheets_properties(&self) -> Vec<SheetProperties> {
1952 self.model.get_worksheets_properties()
1953 }
1954
1955 pub fn set_theme(&mut self, theme: Theme) {
1957 let old_value = self.model.workbook.theme.clone();
1958 let new_value = theme.clone();
1959 self.model.set_theme(theme);
1960 self.push_diff_list(vec![Diff::SetTheme {
1961 old_value: Box::new(old_value),
1962 new_value: Box::new(new_value),
1963 }]);
1964 }
1965
1966 pub fn get_theme(&self) -> Theme {
1968 self.model.get_theme()
1969 }
1970
1971 pub fn resolve_color(&self, color: &Color) -> String {
1974 color.to_rgb(&self.model.workbook.theme)
1975 }
1976
1977 pub fn set_show_grid_lines(&mut self, sheet: u32, show_grid_lines: bool) -> Result<(), String> {
1979 let old_value = self.model.workbook.worksheet(sheet)?.show_grid_lines;
1980 self.model.set_show_grid_lines(sheet, show_grid_lines)?;
1981
1982 self.push_diff_list(vec![Diff::SetShowGridLines {
1983 sheet,
1984 new_value: show_grid_lines,
1985 old_value,
1986 }]);
1987 Ok(())
1988 }
1989
1990 pub fn get_show_grid_lines(&self, sheet: u32) -> Result<bool, String> {
1992 Ok(self.model.workbook.worksheet(sheet)?.show_grid_lines)
1993 }
1994
1995 pub fn get_last_non_empty_in_row_before_column(
1999 &self,
2000 sheet: u32,
2001 row: i32,
2002 column: i32,
2003 ) -> Result<Option<i32>, String> {
2004 let worksheet = self.model.workbook.worksheet(sheet)?;
2005 let mut last_column = None;
2006 for (col, cell) in worksheet.sheet_data.cells_in_row(row) {
2008 if col < column {
2009 if matches!(*cell, Cell::EmptyCell { .. }) {
2010 continue;
2011 }
2012 last_column = Some(col);
2013 }
2014 }
2015 Ok(last_column)
2016 }
2017
2018 pub fn get_first_non_empty_in_row_after_column(
2022 &self,
2023 sheet: u32,
2024 row: i32,
2025 column: i32,
2026 ) -> Result<Option<i32>, String> {
2027 let worksheet = self.model.workbook.worksheet(sheet)?;
2028 for (col, cell) in worksheet.sheet_data.cells_in_row(row) {
2030 if col > column {
2031 if matches!(*cell, Cell::EmptyCell { .. }) {
2032 continue;
2033 }
2034 return Ok(Some(col));
2035 }
2036 }
2037 Ok(None)
2038 }
2039
2040 pub fn get_cell_array_structure(
2042 &self,
2043 sheet: u32,
2044 row: i32,
2045 column: i32,
2046 ) -> Result<CellArrayStructure, String> {
2047 let cell = self
2048 .model
2049 .workbook
2050 .worksheet(sheet)?
2051 .cell(row, column)
2052 .map(Cow::into_owned)
2053 .unwrap_or_default();
2054 match cell {
2055 Cell::EmptyCell { .. }
2056 | Cell::BooleanCell { .. }
2057 | Cell::NumberCell { .. }
2058 | Cell::ErrorCell { .. }
2059 | Cell::SharedString { .. }
2060 | Cell::CellFormula { .. } => Ok(CellArrayStructure::SingleCell),
2061 Cell::SpillCell { a, .. } => {
2062 let (m_row, m_column) = a;
2063 let m_cell = self
2064 .model
2065 .workbook
2066 .worksheet(sheet)?
2067 .cell(m_row, m_column)
2068 .map(Cow::into_owned)
2069 .unwrap_or_default();
2070 let (width, height, is_dynamic) = match m_cell {
2071 Cell::ArrayFormula {
2072 r,
2073 kind: ArrayKind::Dynamic,
2074 ..
2075 } => (r.0, r.1, true),
2076 Cell::ArrayFormula {
2077 r,
2078 kind: ArrayKind::Cse,
2079 ..
2080 } => (r.0, r.1, false),
2081 _ => return Err("Invalid structure".to_string()),
2082 };
2083 if is_dynamic {
2084 Ok(CellArrayStructure::DynamicChild(
2085 m_row, m_column, width, height,
2086 ))
2087 } else {
2088 Ok(CellArrayStructure::ArrayChild(
2089 m_row, m_column, width, height,
2090 ))
2091 }
2092 }
2093 Cell::ArrayFormula {
2094 r,
2095 kind: ArrayKind::Dynamic,
2096 ..
2097 } => Ok(CellArrayStructure::DynamicAnchor(r.0, r.1)),
2098 Cell::ArrayFormula {
2099 r,
2100 kind: ArrayKind::Cse,
2101 ..
2102 } => Ok(CellArrayStructure::ArrayAnchor(r.0, r.1)),
2103 }
2104 }
2105
2106 pub fn set_user_array_formula(
2108 &mut self,
2109 sheet: u32,
2110 row: i32,
2111 column: i32,
2112 width: i32,
2113 height: i32,
2114 formula: &str,
2115 ) -> Result<(), String> {
2116 let ws = self.model.workbook.worksheet(sheet)?;
2117 let mut old_values = Vec::new();
2118 for r in row..row + height {
2119 let mut row_vals = Vec::new();
2120 for c in column..column + width {
2121 let cell = ws.cell(r, c).map(Cow::into_owned);
2122 let cell = if matches!(cell, Some(Cell::SpillCell { .. })) {
2124 None
2125 } else {
2126 cell
2127 };
2128 row_vals.push(cell);
2129 }
2130 old_values.push(row_vals);
2131 }
2132 self.model
2133 .set_user_array_formula(sheet, row, column, width, height, formula)?;
2134 self.push_diff_list(vec![Diff::SetArrayValue {
2135 sheet,
2136 row,
2137 column,
2138 width,
2139 height,
2140 new_value: formula.to_string(),
2141 old_values,
2142 }]);
2143 self.evaluate_if_not_paused();
2144 Ok(())
2145 }
2146
2147 pub fn get_defined_name_list(&self) -> Vec<(String, Option<u32>, String)> {
2149 self.model.get_defined_name_list()
2150 }
2151
2152 pub fn delete_defined_name(&mut self, name: &str, scope: Option<u32>) -> Result<(), String> {
2154 let old_value = self.model.get_defined_name_formula(name, scope)?;
2155 let diff_list = vec![Diff::DeleteDefinedName {
2156 name: name.to_string(),
2157 scope,
2158 old_value,
2159 }];
2160 self.push_diff_list(diff_list);
2161 self.model.delete_defined_name(name, scope)?;
2162 self.evaluate_if_not_paused();
2163 Ok(())
2164 }
2165
2166 pub fn new_defined_name(
2168 &mut self,
2169 name: &str,
2170 scope: Option<u32>,
2171 formula: &str,
2172 ) -> Result<(), String> {
2173 self.model.new_defined_name(name, scope, formula)?;
2174 let value = self.model.get_defined_name_formula(name, scope)?;
2179 let diff_list = vec![Diff::CreateDefinedName {
2180 name: name.to_string(),
2181 scope,
2182 value,
2183 }];
2184 self.push_diff_list(diff_list);
2185 self.evaluate_if_not_paused();
2186 Ok(())
2187 }
2188
2189 pub fn update_defined_name(
2191 &mut self,
2192 name: &str,
2193 scope: Option<u32>,
2194 new_name: &str,
2195 new_scope: Option<u32>,
2196 new_formula: &str,
2197 ) -> Result<(), String> {
2198 let old_formula = self
2201 .model
2202 .get_defined_name_formula(name, scope)
2203 .map_err(|_| "General: Failed to get old name")?;
2204 self.model
2205 .update_defined_name(name, scope, new_name, new_scope, new_formula)?;
2206 let new_formula_internal = self.model.get_defined_name_formula(new_name, new_scope)?;
2209 let diff_list = vec![Diff::UpdateDefinedName {
2210 name: name.to_string(),
2211 scope,
2212 old_formula,
2213 new_name: new_name.to_string(),
2214 new_scope,
2215 new_formula: new_formula_internal,
2216 }];
2217 self.push_diff_list(diff_list);
2218 self.evaluate_if_not_paused();
2219 Ok(())
2220 }
2221
2222 pub fn is_valid_defined_name(
2224 &mut self,
2225 name: &str,
2226 scope: Option<u32>,
2227 formula: &str,
2228 ) -> Result<Option<u32>, String> {
2229 self.model.is_valid_defined_name(name, scope, formula)
2230 }
2231
2232 pub fn set_timezone(&mut self, timezone: &str) -> Result<(), String> {
2234 let diff_list = vec![Diff::SetTimezone {
2235 old_value: self.get_timezone(),
2236 new_value: timezone.to_string(),
2237 }];
2238 self.push_diff_list(diff_list);
2239 self.model.set_timezone(timezone)
2240 }
2241
2242 pub fn set_locale(&mut self, locale: &str) -> Result<(), String> {
2244 let diff_list = vec![Diff::SetLocale {
2245 old_value: self.get_locale(),
2246 new_value: locale.to_string(),
2247 }];
2248 self.push_diff_list(diff_list);
2249 self.model.set_locale(locale)
2250 }
2251
2252 pub fn get_timezone(&self) -> String {
2254 self.model.get_timezone()
2255 }
2256
2257 pub fn get_locale(&self) -> String {
2259 self.model.get_locale()
2260 }
2261
2262 pub fn get_language(&self) -> String {
2264 self.model.get_language()
2265 }
2266
2267 pub fn set_language(&mut self, language: &str) -> Result<(), String> {
2269 self.model.set_language(language)
2270 }
2271
2272 pub fn get_fmt_settings(&self) -> FmtSettings {
2274 self.model.get_fmt_settings()
2275 }
2276
2277 pub(super) fn merged_cells_snapshot_diffs(
2290 &self,
2291 sheet: u32,
2292 old_merged_cells: Vec<MergedCell>,
2293 ) -> Result<Vec<Diff>, String> {
2294 if self.model.get_merged_cells(sheet)? != old_merged_cells.as_slice() {
2295 Ok(vec![Diff::SetMergedCells {
2296 sheet,
2297 old_value: old_merged_cells.clone(),
2298 new_value: old_merged_cells,
2299 }])
2300 } else {
2301 Ok(vec![])
2302 }
2303 }
2304
2305 pub(crate) fn push_diff_list(&mut self, diff_list: DiffList) {
2306 self.send_queue.push(QueueDiffs {
2307 r#type: DiffType::Redo,
2308 list: diff_list.clone(),
2309 });
2310 self.history.push(diff_list);
2311 }
2312
2313 pub(super) fn evaluate_if_not_paused(&mut self) {
2314 if !self.pause_evaluation {
2315 self.model.evaluate();
2316 }
2317 }
2318}
2319
2320#[cfg(test)]
2321mod tests {
2322 use crate::{
2323 types::{HorizontalAlignment, VerticalAlignment},
2324 user_model::common::{horizontal, selected_sheet_after_move, vertical},
2325 };
2326
2327 #[test]
2328 fn test_selected_sheet_after_move() {
2329 assert_eq!(selected_sheet_after_move(0, 0, 2), 2);
2331 assert_eq!(selected_sheet_after_move(3, 3, 0), 0);
2332
2333 assert_eq!(selected_sheet_after_move(2, 0, 2), 1);
2336 assert_eq!(selected_sheet_after_move(0, 2, 0), 1);
2338
2339 assert_eq!(selected_sheet_after_move(3, 1, 2), 3);
2342 }
2343
2344 #[test]
2345 fn test_vertical() {
2346 let all = vec![
2347 VerticalAlignment::Bottom,
2348 VerticalAlignment::Center,
2349 VerticalAlignment::Distributed,
2350 VerticalAlignment::Justify,
2351 VerticalAlignment::Top,
2352 ];
2353 for a in all {
2354 assert_eq!(vertical(&format!("{a}")), Ok(a));
2355 }
2356 }
2357
2358 #[test]
2359 fn test_horizontal() {
2360 let all = vec![
2361 HorizontalAlignment::Center,
2362 HorizontalAlignment::CenterContinuous,
2363 HorizontalAlignment::Distributed,
2364 HorizontalAlignment::Fill,
2365 HorizontalAlignment::General,
2366 HorizontalAlignment::Justify,
2367 HorizontalAlignment::Left,
2368 HorizontalAlignment::Right,
2369 ];
2370 for a in all {
2371 assert_eq!(horizontal(&format!("{a}")), Ok(a));
2372 }
2373 }
2374}