1use crate::cf_types::{CfRule, Cfvo};
2use crate::constants::{LAST_COLUMN, LAST_ROW};
3use crate::cut_paste::cf_sqref_anchor;
4use crate::expressions::parser::stringify::{
5 to_localized_string, to_string_displaced, DisplaceData,
6};
7use crate::expressions::parser::Parser as ExprParser;
8use crate::expressions::types::CellReferenceRC;
9use crate::expressions::utils;
10use crate::language::get_default_language;
11use crate::locale::get_default_locale;
12use crate::model::{CellStructure, Model};
13use crate::types::{ArrayKind, Cell};
14
15fn displace_cf_row(row: i32, data: &DisplaceData, sheet: u32) -> Option<i32> {
17 match data {
18 DisplaceData::Row {
19 sheet: s,
20 row: dr,
21 delta,
22 } if *s == sheet => {
23 if row >= *dr {
24 if *delta < 0 && row < *dr - *delta {
25 None
26 } else {
27 Some(row + *delta)
28 }
29 } else {
30 Some(row)
31 }
32 }
33 DisplaceData::RowMove {
34 sheet: s,
35 row: mr,
36 delta,
37 } if *s == sheet => {
38 if row == *mr {
39 Some(row + *delta)
40 } else if *delta > 0 && row > *mr && row <= *mr + *delta {
41 Some(row - 1)
42 } else if *delta < 0 && row < *mr && row >= *mr + *delta {
43 Some(row + 1)
44 } else {
45 Some(row)
46 }
47 }
48 _ => Some(row),
49 }
50}
51
52fn displace_cf_col(col: i32, data: &DisplaceData, sheet: u32) -> Option<i32> {
54 match data {
55 DisplaceData::Column {
56 sheet: s,
57 column: dc,
58 delta,
59 } if *s == sheet => {
60 if col >= *dc {
61 if *delta < 0 && col < *dc - *delta {
62 None
63 } else {
64 Some(col + *delta)
65 }
66 } else {
67 Some(col)
68 }
69 }
70 DisplaceData::ColumnMove {
71 sheet: s,
72 column: mc,
73 delta,
74 } if *s == sheet => {
75 if col == *mc {
76 Some(col + *delta)
77 } else if *delta > 0 && col > *mc && col <= *mc + *delta {
78 Some(col - 1)
79 } else if *delta < 0 && col < *mc && col >= *mc + *delta {
80 Some(col + 1)
81 } else {
82 Some(col)
83 }
84 }
85 _ => Some(col),
86 }
87}
88
89fn displace_cf_sqref_part(part: &str, data: &DisplaceData, sheet: u32) -> String {
92 let upper = part.to_uppercase();
93 let segs: Vec<&str> = upper.splitn(2, ':').collect();
94 match segs.len() {
95 1 => {
96 if let Some(r) = utils::parse_reference_a1(segs[0]) {
97 if let (Some(nr), Some(nc)) = (
98 displace_cf_row(r.row, data, sheet),
99 displace_cf_col(r.column, data, sheet),
100 ) {
101 if let Some(c) = utils::number_to_column(nc) {
102 return format!("{c}{nr}");
103 }
104 }
105 }
106 part.to_string()
107 }
108 2 => {
109 if let (Some(r1), Some(r2)) = (
110 utils::parse_reference_a1(segs[0]),
111 utils::parse_reference_a1(segs[1]),
112 ) {
113 if let (Some(nr1), Some(nc1), Some(nr2), Some(nc2)) = (
114 displace_cf_row(r1.row, data, sheet),
115 displace_cf_col(r1.column, data, sheet),
116 displace_cf_row(r2.row, data, sheet),
117 displace_cf_col(r2.column, data, sheet),
118 ) {
119 if let (Some(c1), Some(c2)) =
120 (utils::number_to_column(nc1), utils::number_to_column(nc2))
121 {
122 return format!("{c1}{nr1}:{c2}{nr2}");
123 }
124 }
125 }
126 part.to_string()
127 }
128 _ => part.to_string(),
129 }
130}
131
132fn displace_cf_sqref(sqref: &str, data: &DisplaceData, sheet: u32) -> String {
134 sqref
135 .split_whitespace()
136 .map(|p| displace_cf_sqref_part(p, data, sheet))
137 .collect::<Vec<_>>()
138 .join(" ")
139}
140
141fn displace_cf_formula_str(
150 parser: &mut ExprParser<'_>,
151 formula: &str,
152 context: &CellReferenceRC,
153 data: &DisplaceData,
154) -> String {
155 let trimmed = formula.trim();
156 let has_eq = trimmed.starts_with('=');
157 let body = if has_eq { &trimmed[1..] } else { trimmed };
158 let node = parser.parse(body, context);
159 let displaced = to_string_displaced(
160 &node,
161 context,
162 data,
163 get_default_locale(),
164 get_default_language(),
165 );
166 if has_eq {
167 format!("={displaced}")
168 } else {
169 displaced
170 }
171}
172
173fn displace_cfvo(
174 parser: &mut ExprParser<'_>,
175 cfvo: Cfvo,
176 context: &CellReferenceRC,
177 data: &DisplaceData,
178) -> Cfvo {
179 if let Cfvo::Formula(f) = cfvo {
180 Cfvo::Formula(displace_cf_formula_str(parser, &f, context, data))
181 } else {
182 cfvo
183 }
184}
185
186fn displace_cf_rule_formulas(
188 parser: &mut ExprParser<'_>,
189 rule: CfRule,
190 context: &CellReferenceRC,
191 data: &DisplaceData,
192) -> CfRule {
193 match rule {
194 CfRule::Formula {
195 formula,
196 dxf_id,
197 stop_if_true,
198 } => CfRule::Formula {
199 formula: displace_cf_formula_str(parser, &formula, context, data),
200 dxf_id,
201 stop_if_true,
202 },
203 CfRule::CellIs {
204 operator,
205 formula,
206 formula2,
207 dxf_id,
208 stop_if_true,
209 } => CfRule::CellIs {
210 operator,
211 formula: displace_cf_formula_str(parser, &formula, context, data),
212 formula2: formula2.map(|f| displace_cf_formula_str(parser, &f, context, data)),
213 dxf_id,
214 stop_if_true,
215 },
216 CfRule::ColorScale { thresholds } => CfRule::ColorScale {
217 thresholds: thresholds
218 .into_iter()
219 .map(|mut t| {
220 t.cfvo = displace_cfvo(parser, t.cfvo, context, data);
221 t
222 })
223 .collect(),
224 },
225 CfRule::DataBar {
226 min,
227 max,
228 positive_color,
229 negative_color,
230 is_gradient,
231 show_value,
232 } => CfRule::DataBar {
233 min: min.map(|c| displace_cfvo(parser, c, context, data)),
234 max: max.map(|c| displace_cfvo(parser, c, context, data)),
235 positive_color,
236 negative_color,
237 is_gradient,
238 show_value,
239 },
240 CfRule::IconSet {
241 thresholds,
242 show_value,
243 } => CfRule::IconSet {
244 thresholds: thresholds
245 .into_iter()
246 .map(|mut t| {
247 t.cfvo = displace_cfvo(parser, t.cfvo, context, data);
248 t
249 })
250 .collect(),
251 show_value,
252 },
253 CfRule::IconRating {
254 icon,
255 color,
256 thresholds,
257 show_value,
258 } => CfRule::IconRating {
259 icon,
260 color,
261 thresholds: thresholds
262 .into_iter()
263 .map(|(cfvo, strict)| (displace_cfvo(parser, cfvo, context, data), strict))
264 .collect(),
265 show_value,
266 },
267 other => other,
269 }
270}
271
272impl<'a> Model<'a> {
273 fn shift_cell_formula(
274 &mut self,
275 sheet: u32,
276 row: i32,
277 column: i32,
278 displace_data: &DisplaceData,
279 ) -> Result<(), String> {
280 if let Some(f) = self
281 .workbook
282 .worksheet(sheet)?
283 .cell(row, column)
284 .and_then(|c| c.get_formula())
285 {
286 let node = &self.parsed_formulas[sheet as usize][f as usize].0.clone();
287 let cell_reference = CellReferenceRC {
288 sheet: self.workbook.worksheets[sheet as usize].get_name(),
289 row,
290 column,
291 };
292 let formula = to_localized_string(node, &cell_reference, self.locale, self.language);
297 let formula_displaced = to_string_displaced(
298 node,
299 &cell_reference,
300 displace_data,
301 self.locale,
302 self.language,
303 );
304 if formula != formula_displaced {
305 self.update_cell_with_formula(sheet, row, column, format!("={formula_displaced}"))?;
306 };
307 }
308 Ok(())
309 }
310 fn displace_cells(&mut self, displace_data: &DisplaceData) -> Result<(), String> {
316 let cells = self.get_all_cells();
317 for cell in cells {
318 self.shift_cell_formula(cell.index, cell.row, cell.column, displace_data)?;
319 }
320 Ok(())
321 }
322
323 fn displace_cf_ranges(&mut self, sheet: u32, displace_data: &DisplaceData) {
325 let count = match self.workbook.worksheets.get(sheet as usize) {
326 Some(ws) => ws.conditional_formatting.len(),
327 None => return,
328 };
329
330 let sheet_name = self.workbook.worksheets[sheet as usize].get_name();
332 let mut phase1: Vec<(usize, String, CfRule, i32, i32)> = Vec::with_capacity(count);
333 for idx in 0..count {
334 let cf = &self.workbook.worksheets[sheet as usize].conditional_formatting[idx];
335 let old_range = cf.range.clone();
336 let new_range = displace_cf_sqref(&old_range, displace_data, sheet);
337 let rule = cf.cf_rule.clone();
338 if let Some((anchor_row, anchor_col)) = cf_sqref_anchor(&old_range) {
339 phase1.push((idx, new_range, rule, anchor_row, anchor_col));
340 }
341 }
342
343 let locale = self.locale;
347 let language = self.language;
348 self.parser.set_locale(get_default_locale());
349 self.parser.set_language(get_default_language());
350 for (idx, new_range, rule, anchor_row, anchor_col) in phase1 {
351 let context = CellReferenceRC {
352 sheet: sheet_name.clone(),
353 row: anchor_row,
354 column: anchor_col,
355 };
356 let new_rule =
357 displace_cf_rule_formulas(&mut self.parser, rule, &context, displace_data);
358 self.workbook.worksheets[sheet as usize].conditional_formatting[idx].range = new_range;
359 self.workbook.worksheets[sheet as usize].conditional_formatting[idx].cf_rule = new_rule;
360 }
361 self.parser.set_locale(locale);
362 self.parser.set_language(language);
363 }
364
365 fn get_columns_for_row(
379 &self,
380 sheet: u32,
381 row: i32,
382 descending: bool,
383 ) -> Result<Vec<i32>, String> {
384 let worksheet = self.workbook.worksheet(sheet)?;
385 if let Some(row_data) = worksheet.sheet_data.get(&row) {
386 let mut columns: Vec<i32> = row_data.keys().copied().collect();
387 columns.sort_unstable();
388 if descending {
389 columns.reverse();
390 }
391 Ok(columns)
392 } else {
393 Ok(vec![])
394 }
395 }
396
397 fn move_cell(
410 &mut self,
411 sheet: u32,
412 source_row: i32,
413 source_column: i32,
414 target_row: i32,
415 target_column: i32,
416 ) -> Result<(), String> {
417 let source_cell = match self
418 .workbook
419 .worksheet(sheet)?
420 .cell(source_row, source_column)
421 {
422 Some(c) => c,
423 None => return Ok(()),
424 };
425 let style = source_cell.get_style();
426
427 let mut array = None;
428
429 match source_cell {
430 Cell::EmptyCell { .. }
431 | Cell::BooleanCell { .. }
432 | Cell::NumberCell { .. }
433 | Cell::ErrorCell { .. }
434 | Cell::SharedString { .. }
435 | Cell::CellFormula { .. } => {
436 }
438 Cell::SpillCell { .. } => {
439 let worksheet = self.workbook.worksheet_mut(sheet)?;
442 worksheet.remove_cell(source_row, source_column)?;
443 return Ok(());
444 }
445 Cell::ArrayFormula {
446 r,
447 kind: ArrayKind::Dynamic,
448 ..
449 } => {
450 debug_assert_eq!(*r, (1, 1));
453 }
454 Cell::ArrayFormula {
455 r,
456 kind: ArrayKind::Cse,
457 ..
458 } => {
459 array = Some(*r);
462 }
463 }
464 let formula_or_value = self
465 .get_cell_formula(sheet, source_row, source_column)?
466 .unwrap_or_else(|| {
467 source_cell.get_localized_text(
468 &self.workbook.shared_strings,
469 self.locale,
470 self.language,
471 )
472 });
473
474 if let Some((width, height)) = array {
475 self.set_user_array_formula(
477 sheet,
478 target_row,
479 target_column,
480 width,
481 height,
482 &formula_or_value,
483 )?;
484 } else {
485 self.set_user_input(sheet, target_row, target_column, formula_or_value)?;
486 }
487
488 let worksheet = self.workbook.worksheet_mut(sheet)?;
489 worksheet.set_cell_style(target_row, target_column, style)?;
491
492 worksheet.remove_cell(source_row, source_column)?;
494 Ok(())
495 }
496
497 pub fn insert_columns(
507 &mut self,
508 sheet: u32,
509 column: i32,
510 column_count: i32,
511 ) -> Result<(), String> {
512 if column_count <= 0 {
513 return Err("Cannot add a negative number of cells :)".to_string());
514 }
515 if !self.can_insert_columns(sheet, column, column_count)? {
516 return Err(
517 "Cannot insert columns because that would break an array formula".to_string(),
518 );
519 }
520 let dimensions = self.workbook.worksheet(sheet)?.dimension();
522 let last_column = dimensions.max_column + column_count;
523 if last_column > LAST_COLUMN {
524 return Err(
525 "Cannot shift cells because that would delete cells at the end of a row"
526 .to_string(),
527 );
528 }
529 self.reset_dynamic_array_spills(sheet)?;
530 let worksheet = self.workbook.worksheet(sheet)?;
531 let all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
532 for row in all_rows {
533 let sorted_columns = self.get_columns_for_row(sheet, row, true)?;
534 for col in sorted_columns {
535 if col >= column {
536 self.move_cell(sheet, row, col, row, col + column_count)?;
537 } else {
538 break;
540 }
541 }
542 }
543
544 let disp = DisplaceData::Column {
546 sheet,
547 column,
548 delta: column_count,
549 };
550 self.displace_cells(&disp)?;
551 self.displace_cf_ranges(sheet, &disp);
552
553 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
558
559 let mut new_columns = Vec::new();
560 for col in worksheet.cols.iter_mut() {
561 let min = col.min;
563 let max = col.max;
564 if column > max {
565 } else if column <= min {
567 col.min = min + column_count;
569 col.max = max + column_count;
570 } else {
571 col.max = max + column_count;
573 }
574 new_columns.push(col.clone());
575 }
576 worksheet.cols = new_columns;
579
580 Ok(())
581 }
582
583 pub fn delete_columns(
591 &mut self,
592 sheet: u32,
593 column: i32,
594 column_count: i32,
595 ) -> Result<(), String> {
596 if column_count <= 0 {
597 return Err("Please use insert columns instead".to_string());
598 }
599 if !(1..=LAST_COLUMN).contains(&column) {
600 return Err(format!("Column number '{column}' is not valid."));
601 }
602 if column + column_count - 1 > LAST_COLUMN {
603 return Err("Cannot delete columns beyond the last column of the sheet".to_string());
604 }
605 if !self.can_delete_columns(sheet, column, column_count)? {
606 return Err(
607 "Cannot delete columns because that would break an array formula".to_string(),
608 );
609 }
610
611 self.reset_dynamic_array_spills(sheet)?;
612 let column_start = column;
614 let column_end = column + column_count - 1;
616
617 let worksheet = &self.workbook.worksheet(sheet)?;
619 let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
620 all_rows.sort_unstable();
622
623 for r in all_rows {
624 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
625 for col in columns {
626 if col >= column_start {
627 if col > column_end {
628 self.move_cell(sheet, r, col, r, col - column_count)?;
629 } else {
630 self.workbook.worksheet_mut(sheet)?.remove_cell(r, col)?;
631 }
632 }
633 }
634 }
635 let disp = DisplaceData::Column {
637 sheet,
638 column,
639 delta: -column_count,
640 };
641 self.displace_cells(&disp)?;
642 self.displace_cf_ranges(sheet, &disp);
643 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
644
645 let mut new_columns = Vec::new();
647 for col in worksheet.cols.iter_mut() {
648 let min = col.min;
650 let max = col.max;
651 if column_start < min {
663 if column_end < min {
664 let mut new_column = col.clone();
667 new_column.min = min - column_count;
668 new_column.max = max - column_count;
669 new_columns.push(new_column);
670 } else if column_end < max {
671 let mut new_column = col.clone();
674 new_column.min = column_start;
675 new_column.max = max - column_count;
676 new_columns.push(new_column);
677 } else {
678 }
681 } else if column_start <= max {
682 if column_end <= max {
683 let mut new_column = col.clone();
686 new_column.max = max - column_count;
687 new_columns.push(new_column);
688 } else {
689 let mut new_column = col.clone();
691 new_column.max = column_start - 1;
692 new_columns.push(new_column);
693 }
694 } else {
695 new_columns.push(col.clone());
698 }
699 }
700 worksheet.cols = new_columns;
701
702 Ok(())
703 }
704
705 fn can_insert_rows(&self, sheet: u32, row: i32, _row_count: i32) -> Result<bool, String> {
710 let cell_coords: Vec<(i32, i32)> = {
711 let worksheet = self.workbook.worksheet(sheet)?;
712 worksheet
713 .sheet_data
714 .iter()
715 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
716 .collect()
717 };
718 for (r, c) in cell_coords {
719 if let CellStructure::ArrayFormula { range: (_, height) } =
720 self.get_cell_structure(sheet, r, c)?
721 {
722 if r < row && row < r + height {
726 return Ok(false);
727 }
728 }
729 }
730 Ok(true)
731 }
732
733 fn can_insert_columns(
735 &self,
736 sheet: u32,
737 column: i32,
738 _column_count: i32,
739 ) -> Result<bool, String> {
740 let cell_coords: Vec<(i32, i32)> = {
741 let worksheet = self.workbook.worksheet(sheet)?;
742 worksheet
743 .sheet_data
744 .iter()
745 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
746 .collect()
747 };
748 for (r, c) in cell_coords {
749 if let CellStructure::ArrayFormula { range: (width, _) } =
750 self.get_cell_structure(sheet, r, c)?
751 {
752 if c < column && column < c + width {
753 return Ok(false);
754 }
755 }
756 }
757 Ok(true)
758 }
759
760 fn can_delete_rows(&self, sheet: u32, row: i32, row_count: i32) -> Result<bool, String> {
764 let row_end = row + row_count; let cell_coords: Vec<(i32, i32)> = {
766 let worksheet = self.workbook.worksheet(sheet)?;
767 worksheet
768 .sheet_data
769 .iter()
770 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
771 .collect()
772 };
773 for (r, c) in cell_coords {
774 if let CellStructure::ArrayFormula { range: (_, height) } =
775 self.get_cell_structure(sheet, r, c)?
776 {
777 let overlaps = r < row_end && r + height > row;
779 let contained = r >= row && r + height <= row_end;
780 if overlaps && !contained {
781 return Ok(false);
782 }
783 }
784 }
785 Ok(true)
786 }
787
788 fn can_delete_columns(
791 &self,
792 sheet: u32,
793 column: i32,
794 column_count: i32,
795 ) -> Result<bool, String> {
796 let col_end = column + column_count; let cell_coords: Vec<(i32, i32)> = {
798 let worksheet = self.workbook.worksheet(sheet)?;
799 worksheet
800 .sheet_data
801 .iter()
802 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
803 .collect()
804 };
805 for (r, c) in cell_coords {
806 if let CellStructure::ArrayFormula { range: (width, _) } =
807 self.get_cell_structure(sheet, r, c)?
808 {
809 let overlaps = c < col_end && c + width > column;
811 let contained = c >= column && c + width <= col_end;
812 if overlaps && !contained {
813 return Ok(false);
814 }
815 }
816 }
817 Ok(true)
818 }
819
820 pub fn insert_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
828 if row_count <= 0 {
829 return Err("Cannot add a negative number of cells :)".to_string());
830 }
831 if !self.can_insert_rows(sheet, row, row_count)? {
832 return Err("Cannot insert rows because that would break an array formula".to_string());
833 }
834 let dimensions = self.workbook.worksheet(sheet)?.dimension();
836 let last_row = dimensions.max_row + row_count;
837 if last_row > LAST_ROW {
838 return Err(
839 "Cannot shift cells because that would delete cells at the end of a column"
840 .to_string(),
841 );
842 }
843
844 self.reset_dynamic_array_spills(sheet)?;
845 let worksheet = &self.workbook.worksheet(sheet)?;
847 let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
848 all_rows.sort_unstable();
849 all_rows.reverse();
850 for r in all_rows {
851 if r >= row {
852 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
854 for column in columns {
855 self.move_cell(sheet, r, column, r + row_count, column)?;
856 }
857 } else {
858 break;
860 }
861 }
862 let rows = &self.workbook.worksheets[sheet as usize].rows;
866 let mut new_rows = vec![];
867 for r in rows {
868 if r.r < row {
869 new_rows.push(r.clone());
870 } else if r.r >= row {
871 let mut new_row = r.clone();
872 new_row.r = r.r + row_count;
873 new_rows.push(new_row);
874 }
875 }
876 self.workbook.worksheets[sheet as usize].rows = new_rows;
877
878 let disp = DisplaceData::Row {
880 sheet,
881 row,
882 delta: row_count,
883 };
884 self.displace_cells(&disp)?;
885 self.displace_cf_ranges(sheet, &disp);
886
887 Ok(())
888 }
889
890 pub fn delete_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
898 if row_count <= 0 {
899 return Err("Please use insert rows instead".to_string());
900 }
901 if !(1..=LAST_ROW).contains(&row) {
902 return Err(format!("Row number '{row}' is not valid."));
903 }
904 if row + row_count - 1 > LAST_ROW {
905 return Err("Cannot delete rows beyond the last row of the sheet".to_string());
906 }
907 if !self.can_delete_rows(sheet, row, row_count)? {
908 return Err("Cannot delete rows because that would break an array formula".to_string());
909 }
910
911 self.reset_dynamic_array_spills(sheet)?;
912 let worksheet = &self.workbook.worksheet(sheet)?;
914 let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
915 all_rows.sort_unstable();
916
917 for r in all_rows {
918 if r >= row {
919 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
921 if r >= row + row_count {
922 for column in columns {
924 self.move_cell(sheet, r, column, r - row_count, column)?;
925 }
926 } else {
927 self.workbook.worksheet_mut(sheet)?.sheet_data.remove(&r);
929 }
930 }
931 }
932 let rows = &self.workbook.worksheets[sheet as usize].rows;
937 let mut new_rows = vec![];
938 for r in rows {
939 if r.r < row {
940 new_rows.push(r.clone());
941 } else if r.r >= row + row_count {
942 let mut new_row = r.clone();
943 new_row.r = r.r - row_count;
944 new_rows.push(new_row);
945 }
946 }
947 self.workbook.worksheets[sheet as usize].rows = new_rows;
948 let disp = DisplaceData::Row {
949 sheet,
950 row,
951 delta: -row_count,
952 };
953 self.displace_cells(&disp)?;
954 self.displace_cf_ranges(sheet, &disp);
955 Ok(())
956 }
957
958 fn move_column_unchecked(&mut self, sheet: u32, column: i32, delta: i32) -> Result<(), String> {
961 let target_column = column + delta;
962 let original_refs = self
963 .workbook
964 .worksheet(sheet)?
965 .column_cell_references(column)?;
966 let mut original_cells = Vec::new();
967 for r in &original_refs {
968 let cell = self
969 .workbook
970 .worksheet(sheet)?
971 .cell(r.row, column)
972 .ok_or("Expected Cell to exist")?;
973 let style_idx = cell.get_style();
974 let formula_or_value =
975 self.get_cell_formula(sheet, r.row, column)?
976 .unwrap_or_else(|| {
977 cell.get_localized_text(
978 &self.workbook.shared_strings,
979 self.locale,
980 self.language,
981 )
982 });
983
984 let mut array = None;
985
986 match cell {
987 Cell::EmptyCell { .. }
988 | Cell::BooleanCell { .. }
989 | Cell::NumberCell { .. }
990 | Cell::ErrorCell { .. }
991 | Cell::SharedString { .. }
992 | Cell::CellFormula { .. } => {
993 }
995 Cell::SpillCell { .. } => {
996 let worksheet = self.workbook.worksheet_mut(sheet)?;
999 worksheet.remove_cell(r.row, column)?;
1000 continue;
1001 }
1002 Cell::ArrayFormula {
1003 r,
1004 kind: ArrayKind::Dynamic,
1005 ..
1006 } => {
1007 debug_assert_eq!(*r, (1, 1));
1010 }
1011 Cell::ArrayFormula {
1012 r,
1013 kind: ArrayKind::Cse,
1014 ..
1015 } => {
1016 array = Some(*r);
1019 }
1020 }
1021
1022 original_cells.push((r.row, formula_or_value, style_idx, array));
1023 let ws = self.workbook.worksheet_mut(sheet)?;
1024 ws.remove_cell(r.row, column)?;
1025 }
1026 let width = self
1027 .workbook
1028 .worksheet(sheet)?
1029 .get_actual_column_width(column)?;
1030 let style = self.workbook.worksheet(sheet)?.get_column_style(column)?;
1031 let hidden = self.workbook.worksheet(sheet)?.is_column_hidden(column)?;
1032 if delta > 0 {
1033 for c in column + 1..=target_column {
1034 let refs = self.workbook.worksheet(sheet)?.column_cell_references(c)?;
1035 for r in refs {
1036 self.move_cell(sheet, r.row, c, r.row, c - 1)?;
1037 }
1038 let w = self.workbook.worksheet(sheet)?.get_actual_column_width(c)?;
1039 let s = self.workbook.worksheet(sheet)?.get_column_style(c)?;
1040 let h = self.workbook.worksheet(sheet)?.is_column_hidden(c)?;
1041 self.workbook
1042 .worksheet_mut(sheet)?
1043 .set_column_width_and_style(c - 1, w, h, s)?;
1044 }
1045 } else {
1046 for c in (target_column..=column - 1).rev() {
1047 let refs = self.workbook.worksheet(sheet)?.column_cell_references(c)?;
1048 for r in refs {
1049 self.move_cell(sheet, r.row, c, r.row, c + 1)?;
1050 }
1051 let w = self.workbook.worksheet(sheet)?.get_actual_column_width(c)?;
1052 let s = self.workbook.worksheet(sheet)?.get_column_style(c)?;
1053 let h = self.workbook.worksheet(sheet)?.is_column_hidden(c)?;
1054 self.workbook
1055 .worksheet_mut(sheet)?
1056 .set_column_width_and_style(c + 1, w, h, s)?;
1057 }
1058 }
1059 for (r, value, style_idx, array) in original_cells {
1060 if let Some(a) = array {
1061 self.set_user_array_formula(sheet, r, target_column, a.0, a.1, &value)?;
1062 } else {
1063 self.set_user_input(sheet, r, target_column, value)?;
1064 }
1065 self.workbook
1066 .worksheet_mut(sheet)?
1067 .set_cell_style(r, target_column, style_idx)?;
1068 }
1069 self.workbook
1070 .worksheet_mut(sheet)?
1071 .set_column_width_and_style(target_column, width, hidden, style)?;
1072 let disp = DisplaceData::ColumnMove {
1073 sheet,
1074 column,
1075 delta,
1076 };
1077 self.displace_cells(&disp)?;
1078 self.displace_cf_ranges(sheet, &disp);
1079 Ok(())
1080 }
1081
1082 fn move_row_unchecked(&mut self, sheet: u32, row: i32, delta: i32) -> Result<(), String> {
1084 let target_row = row + delta;
1085 let original_cols = self.get_columns_for_row(sheet, row, false)?;
1086 let mut original_cells = Vec::new();
1087 for c in &original_cols {
1088 let cell = self
1089 .workbook
1090 .worksheet(sheet)?
1091 .cell(row, *c)
1092 .ok_or("Expected Cell to exist")?;
1093 let style_idx = cell.get_style();
1094 let formula_or_value = self.get_cell_formula(sheet, row, *c)?.unwrap_or_else(|| {
1095 cell.get_localized_text(&self.workbook.shared_strings, self.locale, self.language)
1096 });
1097 let mut array = None;
1098
1099 match cell {
1100 Cell::EmptyCell { .. }
1101 | Cell::BooleanCell { .. }
1102 | Cell::NumberCell { .. }
1103 | Cell::ErrorCell { .. }
1104 | Cell::SharedString { .. }
1105 | Cell::CellFormula { .. } => {
1106 }
1108 Cell::SpillCell { .. } => {
1109 let worksheet = self.workbook.worksheet_mut(sheet)?;
1112 worksheet.remove_cell(row, *c)?;
1113 continue;
1114 }
1115 Cell::ArrayFormula {
1116 r,
1117 kind: ArrayKind::Dynamic,
1118 ..
1119 } => {
1120 debug_assert_eq!(*r, (1, 1));
1123 }
1124 Cell::ArrayFormula {
1125 r,
1126 kind: ArrayKind::Cse,
1127 ..
1128 } => {
1129 array = Some(*r);
1132 }
1133 }
1134 original_cells.push((*c, formula_or_value, style_idx, array));
1135 let ws = self.workbook.worksheet_mut(sheet)?;
1136 ws.remove_cell(row, *c)?;
1137 }
1138 if delta > 0 {
1139 for r in row + 1..=target_row {
1140 let cols = self.get_columns_for_row(sheet, r, false)?;
1141 for c in cols {
1142 self.move_cell(sheet, r, c, r - 1, c)?;
1143 }
1144 }
1145 } else {
1146 for r in (target_row..=row - 1).rev() {
1147 let cols = self.get_columns_for_row(sheet, r, false)?;
1148 for c in cols {
1149 self.move_cell(sheet, r, c, r + 1, c)?;
1150 }
1151 }
1152 }
1153 for (c, value, style_idx, array) in original_cells {
1154 if let Some(array_range) = array {
1155 self.set_user_array_formula(
1156 sheet,
1157 target_row,
1158 c,
1159 array_range.0,
1160 array_range.1,
1161 &value,
1162 )?;
1163 } else {
1164 self.set_user_input(sheet, target_row, c, value)?;
1165 }
1166 self.workbook
1167 .worksheet_mut(sheet)?
1168 .set_cell_style(target_row, c, style_idx)?;
1169 }
1170 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
1171 let mut new_rows = Vec::new();
1172 for r in worksheet.rows.iter() {
1173 if r.r == row {
1174 let mut nr = r.clone();
1175 nr.r = target_row;
1176 new_rows.push(nr);
1177 } else if delta > 0 && r.r > row && r.r <= target_row {
1178 let mut nr = r.clone();
1179 nr.r -= 1;
1180 new_rows.push(nr);
1181 } else if delta < 0 && r.r < row && r.r >= target_row {
1182 let mut nr = r.clone();
1183 nr.r += 1;
1184 new_rows.push(nr);
1185 } else {
1186 new_rows.push(r.clone());
1187 }
1188 }
1189 worksheet.rows = new_rows;
1190 let disp = DisplaceData::RowMove { sheet, row, delta };
1191 self.displace_cells(&disp)?;
1192 self.displace_cf_ranges(sheet, &disp);
1193 Ok(())
1194 }
1195
1196 fn can_move_columns_action(
1200 &self,
1201 sheet: u32,
1202 column: i32,
1203 column_count: i32,
1204 delta: i32,
1205 ) -> Result<bool, String> {
1206 if delta == 0 {
1207 return Ok(true);
1208 }
1209
1210 let group_start = column;
1211 let group_end = column + column_count - 1;
1212
1213 let (displace_start, displace_end) = if delta > 0 {
1214 (group_end + 1, group_end + delta)
1215 } else {
1216 (group_start + delta, group_start - 1)
1217 };
1218
1219 let overlaps = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1220 a_start <= b_end && b_start <= a_end
1221 };
1222
1223 let contains = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1224 a_start <= b_start && b_end <= a_end
1225 };
1226
1227 let interval_is_safe = |array_start: i32, array_end: i32| {
1228 let safe_for = |start: i32, end: i32| {
1229 !overlaps(start, end, array_start, array_end)
1230 || contains(start, end, array_start, array_end)
1231 };
1232 safe_for(group_start, group_end) && safe_for(displace_start, displace_end)
1233 };
1234
1235 let cell_coords: Vec<(i32, i32)> = {
1236 let worksheet = self.workbook.worksheet(sheet)?;
1237 worksheet
1238 .sheet_data
1239 .iter()
1240 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
1241 .collect()
1242 };
1243
1244 for (r, c) in cell_coords {
1245 match self.get_cell_structure(sheet, r, c)? {
1246 CellStructure::ArrayFormula { range } => {
1247 let (width, _) = range;
1248 let array_start_col = c;
1249 let array_end_col = c + width - 1;
1250
1251 if !interval_is_safe(array_start_col, array_end_col) {
1252 return Ok(false);
1253 }
1254 }
1255 CellStructure::SpillArray { anchor, range } => {
1256 let (width, _) = range;
1257 let (_, array_start_col) = anchor;
1258 let array_end_col = array_start_col + width - 1;
1259
1260 if !interval_is_safe(array_start_col, array_end_col) {
1261 return Ok(false);
1262 }
1263 }
1264 _ => {}
1265 }
1266 }
1267
1268 Ok(true)
1269 }
1270
1271 fn can_move_rows_action(
1277 &self,
1278 sheet: u32,
1279 row: i32,
1280 row_count: i32,
1281 delta: i32,
1282 ) -> Result<bool, String> {
1283 if delta == 0 {
1284 return Ok(true);
1285 }
1286
1287 let group_start = row;
1288 let group_end = row + row_count - 1;
1289
1290 let (displace_start, displace_end) = if delta > 0 {
1291 (group_end + 1, group_end + delta)
1292 } else {
1293 (group_start + delta, group_start - 1)
1294 };
1295
1296 let overlaps = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1297 a_start <= b_end && b_start <= a_end
1298 };
1299
1300 let contains = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1301 a_start <= b_start && b_end <= a_end
1302 };
1303
1304 let interval_is_safe = |array_start: i32, array_end: i32| {
1305 let safe_for = |start: i32, end: i32| {
1306 !overlaps(start, end, array_start, array_end)
1307 || contains(start, end, array_start, array_end)
1308 };
1309
1310 safe_for(group_start, group_end) && safe_for(displace_start, displace_end)
1311 };
1312
1313 let cell_coords: Vec<(i32, i32)> = {
1315 let worksheet = self.workbook.worksheet(sheet)?;
1316 worksheet
1317 .sheet_data
1318 .iter()
1319 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
1320 .collect()
1321 };
1322
1323 for (r, c) in cell_coords {
1324 match self.get_cell_structure(sheet, r, c)? {
1325 CellStructure::ArrayFormula { range } => {
1326 let (_, height) = range;
1327 let array_start_row = r;
1328 let array_end_row = r + height - 1;
1329
1330 if !interval_is_safe(array_start_row, array_end_row) {
1331 return Ok(false);
1332 }
1333 }
1334 CellStructure::SpillArray { anchor, range } => {
1335 let (_, height) = range;
1336 let (array_start_row, _) = anchor;
1337 let array_end_row = array_start_row + height - 1;
1338
1339 if !interval_is_safe(array_start_row, array_end_row) {
1340 return Ok(false);
1341 }
1342 }
1343 _ => {}
1344 }
1345 }
1346
1347 Ok(true)
1348 }
1349
1350 pub fn move_columns_action(
1365 &mut self,
1366 sheet: u32,
1367 column: i32,
1368 column_count: i32,
1369 delta: i32,
1370 ) -> Result<(), String> {
1371 if column_count <= 0 || delta == 0 {
1372 return Ok(());
1373 }
1374 let target_first = column + delta;
1375 let target_last = column + column_count - 1 + delta;
1376 if !(1..=LAST_COLUMN).contains(&target_first) || !(1..=LAST_COLUMN).contains(&target_last) {
1377 return Err("Target column out of boundaries".to_string());
1378 }
1379 if !(1..=LAST_COLUMN).contains(&column)
1380 || !(1..=LAST_COLUMN).contains(&(column + column_count - 1))
1381 {
1382 return Err("Initial column out of boundaries".to_string());
1383 }
1384 if !self.can_move_columns_action(sheet, column, column_count, delta)? {
1385 return Err(
1386 "Cannot move columns because that would split an array formula".to_string(),
1387 );
1388 }
1389 self.reset_dynamic_array_spills(sheet)?;
1390
1391 if delta > 0 {
1393 for col in (column..column + column_count).rev() {
1394 self.move_column_unchecked(sheet, col, delta)?;
1395 }
1396 } else {
1397 for col in column..column + column_count {
1398 self.move_column_unchecked(sheet, col, delta)?;
1399 }
1400 }
1401
1402 Ok(())
1403 }
1404
1405 pub fn move_rows_action(
1412 &mut self,
1413 sheet: u32,
1414 row: i32,
1415 row_count: i32,
1416 delta: i32,
1417 ) -> Result<(), String> {
1418 if row_count <= 0 || delta == 0 {
1419 return Ok(());
1420 }
1421 let target_first = row + delta;
1422 let target_last = row + row_count - 1 + delta;
1423 if !(1..=LAST_ROW).contains(&target_first) || !(1..=LAST_ROW).contains(&target_last) {
1424 return Err("Target row out of boundaries".to_string());
1425 }
1426 if !(1..=LAST_ROW).contains(&row) || !(1..=LAST_ROW).contains(&(row + row_count - 1)) {
1427 return Err("Initial row out of boundaries".to_string());
1428 }
1429 if !self.can_move_rows_action(sheet, row, row_count, delta)? {
1430 return Err("Cannot move rows because that would split an array formula".to_string());
1431 }
1432 self.reset_dynamic_array_spills(sheet)?;
1433
1434 if delta > 0 {
1436 for r in (row..row + row_count).rev() {
1437 self.move_row_unchecked(sheet, r, delta)?;
1438 }
1439 } else {
1440 for r in row..row + row_count {
1441 self.move_row_unchecked(sheet, r, delta)?;
1442 }
1443 }
1444 Ok(())
1445 }
1446}