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, Link, Worksheet};
14
15fn displace_links<F>(worksheet: &mut Worksheet, map: F)
19where
20 F: Fn(i32, i32) -> Option<(i32, i32)>,
21{
22 let links = std::mem::take(&mut worksheet.links);
23 worksheet.links = links
24 .into_iter()
25 .filter_map(|((row, column), link)| map(row, column).map(|key| (key, link)))
26 .collect();
27}
28
29fn displace_cf_row(row: i32, data: &DisplaceData, sheet: u32) -> Option<i32> {
31 match data {
32 DisplaceData::Row {
33 sheet: s,
34 row: dr,
35 delta,
36 } if *s == sheet => {
37 if row >= *dr {
38 if *delta < 0 && row < *dr - *delta {
39 None
40 } else {
41 Some(row + *delta)
42 }
43 } else {
44 Some(row)
45 }
46 }
47 DisplaceData::RowMove {
48 sheet: s,
49 row: mr,
50 delta,
51 } if *s == sheet => {
52 if row == *mr {
53 Some(row + *delta)
54 } else if *delta > 0 && row > *mr && row <= *mr + *delta {
55 Some(row - 1)
56 } else if *delta < 0 && row < *mr && row >= *mr + *delta {
57 Some(row + 1)
58 } else {
59 Some(row)
60 }
61 }
62 _ => Some(row),
63 }
64}
65
66fn displace_cf_col(col: i32, data: &DisplaceData, sheet: u32) -> Option<i32> {
68 match data {
69 DisplaceData::Column {
70 sheet: s,
71 column: dc,
72 delta,
73 } if *s == sheet => {
74 if col >= *dc {
75 if *delta < 0 && col < *dc - *delta {
76 None
77 } else {
78 Some(col + *delta)
79 }
80 } else {
81 Some(col)
82 }
83 }
84 DisplaceData::ColumnMove {
85 sheet: s,
86 column: mc,
87 delta,
88 } if *s == sheet => {
89 if col == *mc {
90 Some(col + *delta)
91 } else if *delta > 0 && col > *mc && col <= *mc + *delta {
92 Some(col - 1)
93 } else if *delta < 0 && col < *mc && col >= *mc + *delta {
94 Some(col + 1)
95 } else {
96 Some(col)
97 }
98 }
99 _ => Some(col),
100 }
101}
102
103fn displace_cf_sqref_part(part: &str, data: &DisplaceData, sheet: u32) -> String {
106 let upper = part.to_uppercase();
107 let segs: Vec<&str> = upper.splitn(2, ':').collect();
108 match segs.len() {
109 1 => {
110 if let Some(r) = utils::parse_reference_a1(segs[0]) {
111 if let (Some(nr), Some(nc)) = (
112 displace_cf_row(r.row, data, sheet),
113 displace_cf_col(r.column, data, sheet),
114 ) {
115 if let Some(c) = utils::number_to_column(nc) {
116 return format!("{c}{nr}");
117 }
118 }
119 }
120 part.to_string()
121 }
122 2 => {
123 if let (Some(r1), Some(r2)) = (
124 utils::parse_reference_a1(segs[0]),
125 utils::parse_reference_a1(segs[1]),
126 ) {
127 if let (Some(nr1), Some(nc1), Some(nr2), Some(nc2)) = (
128 displace_cf_row(r1.row, data, sheet),
129 displace_cf_col(r1.column, data, sheet),
130 displace_cf_row(r2.row, data, sheet),
131 displace_cf_col(r2.column, data, sheet),
132 ) {
133 if let (Some(c1), Some(c2)) =
134 (utils::number_to_column(nc1), utils::number_to_column(nc2))
135 {
136 return format!("{c1}{nr1}:{c2}{nr2}");
137 }
138 }
139 }
140 part.to_string()
141 }
142 _ => part.to_string(),
143 }
144}
145
146fn displace_cf_sqref(sqref: &str, data: &DisplaceData, sheet: u32) -> String {
148 sqref
149 .split_whitespace()
150 .map(|p| displace_cf_sqref_part(p, data, sheet))
151 .collect::<Vec<_>>()
152 .join(" ")
153}
154
155fn displace_cf_formula_str(
164 parser: &mut ExprParser<'_>,
165 formula: &str,
166 context: &CellReferenceRC,
167 data: &DisplaceData,
168) -> String {
169 let trimmed = formula.trim();
170 let has_eq = trimmed.starts_with('=');
171 let body = if has_eq { &trimmed[1..] } else { trimmed };
172 let node = parser.parse(body, context);
173 let displaced = to_string_displaced(
174 &node,
175 context,
176 data,
177 get_default_locale(),
178 get_default_language(),
179 );
180 if has_eq {
181 format!("={displaced}")
182 } else {
183 displaced
184 }
185}
186
187fn displace_cfvo(
188 parser: &mut ExprParser<'_>,
189 cfvo: Cfvo,
190 context: &CellReferenceRC,
191 data: &DisplaceData,
192) -> Cfvo {
193 if let Cfvo::Formula(f) = cfvo {
194 Cfvo::Formula(displace_cf_formula_str(parser, &f, context, data))
195 } else {
196 cfvo
197 }
198}
199
200fn displace_cf_rule_formulas(
202 parser: &mut ExprParser<'_>,
203 rule: CfRule,
204 context: &CellReferenceRC,
205 data: &DisplaceData,
206) -> CfRule {
207 match rule {
208 CfRule::Formula {
209 formula,
210 dxf_id,
211 stop_if_true,
212 } => CfRule::Formula {
213 formula: displace_cf_formula_str(parser, &formula, context, data),
214 dxf_id,
215 stop_if_true,
216 },
217 CfRule::CellIs {
218 operator,
219 formula,
220 formula2,
221 dxf_id,
222 stop_if_true,
223 } => CfRule::CellIs {
224 operator,
225 formula: displace_cf_formula_str(parser, &formula, context, data),
226 formula2: formula2.map(|f| displace_cf_formula_str(parser, &f, context, data)),
227 dxf_id,
228 stop_if_true,
229 },
230 CfRule::ColorScale { thresholds } => CfRule::ColorScale {
231 thresholds: thresholds
232 .into_iter()
233 .map(|mut t| {
234 t.cfvo = displace_cfvo(parser, t.cfvo, context, data);
235 t
236 })
237 .collect(),
238 },
239 CfRule::DataBar {
240 min,
241 max,
242 positive_color,
243 negative_color,
244 is_gradient,
245 show_value,
246 } => CfRule::DataBar {
247 min: min.map(|c| displace_cfvo(parser, c, context, data)),
248 max: max.map(|c| displace_cfvo(parser, c, context, data)),
249 positive_color,
250 negative_color,
251 is_gradient,
252 show_value,
253 },
254 CfRule::IconSet {
255 thresholds,
256 show_value,
257 } => CfRule::IconSet {
258 thresholds: thresholds
259 .into_iter()
260 .map(|mut t| {
261 t.cfvo = displace_cfvo(parser, t.cfvo, context, data);
262 t
263 })
264 .collect(),
265 show_value,
266 },
267 CfRule::IconRating {
268 icon,
269 color,
270 thresholds,
271 show_value,
272 } => CfRule::IconRating {
273 icon,
274 color,
275 thresholds: thresholds
276 .into_iter()
277 .map(|(cfvo, strict)| (displace_cfvo(parser, cfvo, context, data), strict))
278 .collect(),
279 show_value,
280 },
281 other => other,
283 }
284}
285
286impl<'a> Model<'a> {
287 fn shift_cell_formula(
288 &mut self,
289 sheet: u32,
290 row: i32,
291 column: i32,
292 displace_data: &DisplaceData,
293 ) -> Result<(), String> {
294 if let Some(f) = self
295 .workbook
296 .worksheet(sheet)?
297 .cell(row, column)
298 .and_then(|c| c.get_formula())
299 {
300 let node = &self.parsed_formulas[sheet as usize][f as usize].0.clone();
301 let cell_reference = CellReferenceRC {
302 sheet: self.workbook.worksheets[sheet as usize].get_name(),
303 row,
304 column,
305 };
306 let formula = to_localized_string(node, &cell_reference, self.locale, self.language);
311 let formula_displaced = to_string_displaced(
312 node,
313 &cell_reference,
314 displace_data,
315 self.locale,
316 self.language,
317 );
318 if formula != formula_displaced {
319 self.update_cell_with_formula(sheet, row, column, format!("={formula_displaced}"))?;
320 };
321 }
322 Ok(())
323 }
324 fn displace_cells(&mut self, displace_data: &DisplaceData) -> Result<(), String> {
330 let cells = self.get_all_cells();
331 for cell in cells {
332 self.shift_cell_formula(cell.index, cell.row, cell.column, displace_data)?;
333 }
334 Ok(())
335 }
336
337 fn displace_cf_ranges(&mut self, sheet: u32, displace_data: &DisplaceData) {
339 let count = match self.workbook.worksheets.get(sheet as usize) {
340 Some(ws) => ws.conditional_formatting.len(),
341 None => return,
342 };
343
344 let sheet_name = self.workbook.worksheets[sheet as usize].get_name();
346 let mut phase1: Vec<(usize, String, CfRule, i32, i32)> = Vec::with_capacity(count);
347 for idx in 0..count {
348 let cf = &self.workbook.worksheets[sheet as usize].conditional_formatting[idx];
349 let old_range = cf.range.clone();
350 let new_range = displace_cf_sqref(&old_range, displace_data, sheet);
351 let rule = cf.cf_rule.clone();
352 if let Some((anchor_row, anchor_col)) = cf_sqref_anchor(&old_range) {
353 phase1.push((idx, new_range, rule, anchor_row, anchor_col));
354 }
355 }
356
357 let locale = self.locale;
361 let language = self.language;
362 self.parser.set_locale(get_default_locale());
363 self.parser.set_language(get_default_language());
364 for (idx, new_range, rule, anchor_row, anchor_col) in phase1 {
365 let context = CellReferenceRC {
366 sheet: sheet_name.clone(),
367 row: anchor_row,
368 column: anchor_col,
369 };
370 let new_rule =
371 displace_cf_rule_formulas(&mut self.parser, rule, &context, displace_data);
372 self.workbook.worksheets[sheet as usize].conditional_formatting[idx].range = new_range;
373 self.workbook.worksheets[sheet as usize].conditional_formatting[idx].cf_rule = new_rule;
374 }
375 self.parser.set_locale(locale);
376 self.parser.set_language(language);
377 }
378
379 fn get_columns_for_row(
393 &self,
394 sheet: u32,
395 row: i32,
396 descending: bool,
397 ) -> Result<Vec<i32>, String> {
398 let worksheet = self.workbook.worksheet(sheet)?;
399 if let Some(row_data) = worksheet.sheet_data.get(&row) {
400 let mut columns: Vec<i32> = row_data.keys().copied().collect();
401 columns.sort_unstable();
402 if descending {
403 columns.reverse();
404 }
405 Ok(columns)
406 } else {
407 Ok(vec![])
408 }
409 }
410
411 fn move_cell(
424 &mut self,
425 sheet: u32,
426 source_row: i32,
427 source_column: i32,
428 target_row: i32,
429 target_column: i32,
430 ) -> Result<(), String> {
431 let source_cell = match self
432 .workbook
433 .worksheet(sheet)?
434 .cell(source_row, source_column)
435 {
436 Some(c) => c,
437 None => return Ok(()),
438 };
439 let style = source_cell.get_style();
440
441 let mut array = None;
442
443 match source_cell {
444 Cell::EmptyCell { .. }
445 | Cell::BooleanCell { .. }
446 | Cell::NumberCell { .. }
447 | Cell::ErrorCell { .. }
448 | Cell::SharedString { .. }
449 | Cell::CellFormula { .. } => {
450 }
452 Cell::SpillCell { .. } => {
453 let worksheet = self.workbook.worksheet_mut(sheet)?;
456 worksheet.remove_cell(source_row, source_column)?;
457 return Ok(());
458 }
459 Cell::ArrayFormula {
460 r,
461 kind: ArrayKind::Dynamic,
462 ..
463 } => {
464 debug_assert_eq!(*r, (1, 1));
467 }
468 Cell::ArrayFormula {
469 r,
470 kind: ArrayKind::Cse,
471 ..
472 } => {
473 array = Some(*r);
476 }
477 }
478 let formula_or_value = self
479 .get_cell_formula(sheet, source_row, source_column)?
480 .unwrap_or_else(|| {
481 source_cell.get_localized_text(
482 &self.workbook.shared_strings,
483 self.locale,
484 self.language,
485 )
486 });
487
488 if let Some((width, height)) = array {
489 self.set_user_array_formula(
491 sheet,
492 target_row,
493 target_column,
494 width,
495 height,
496 &formula_or_value,
497 )?;
498 } else {
499 self.set_user_input(sheet, target_row, target_column, formula_or_value)?;
500 }
501
502 let worksheet = self.workbook.worksheet_mut(sheet)?;
503 worksheet.set_cell_style(target_row, target_column, style)?;
505
506 worksheet.remove_cell(source_row, source_column)?;
508 Ok(())
509 }
510
511 pub fn insert_columns(
521 &mut self,
522 sheet: u32,
523 column: i32,
524 column_count: i32,
525 ) -> Result<(), String> {
526 if column_count <= 0 {
527 return Err("Cannot add a negative number of cells :)".to_string());
528 }
529 if !self.can_insert_columns(sheet, column, column_count)? {
530 return Err(
531 "Cannot insert columns because that would break an array formula".to_string(),
532 );
533 }
534 let dimensions = self.workbook.worksheet(sheet)?.dimension();
536 let last_column = dimensions.max_column + column_count;
537 if last_column > LAST_COLUMN {
538 return Err(
539 "Cannot shift cells because that would delete cells at the end of a row"
540 .to_string(),
541 );
542 }
543 self.reset_dynamic_array_spills(sheet)?;
544 let worksheet = self.workbook.worksheet(sheet)?;
545 let all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
546 for row in all_rows {
547 let sorted_columns = self.get_columns_for_row(sheet, row, true)?;
548 for col in sorted_columns {
549 if col >= column {
550 self.move_cell(sheet, row, col, row, col + column_count)?;
551 } else {
552 break;
554 }
555 }
556 }
557
558 displace_links(self.workbook.worksheet_mut(sheet)?, |r, c| {
560 if c >= column {
561 Some((r, c + column_count))
562 } else {
563 Some((r, c))
564 }
565 });
566
567 let disp = DisplaceData::Column {
569 sheet,
570 column,
571 delta: column_count,
572 };
573 self.displace_cells(&disp)?;
574 self.displace_cf_ranges(sheet, &disp);
575
576 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
581
582 let mut new_columns = Vec::new();
583 for col in worksheet.cols.iter_mut() {
584 let min = col.min;
586 let max = col.max;
587 if column > max {
588 } else if column <= min {
590 col.min = min + column_count;
592 col.max = max + column_count;
593 } else {
594 col.max = max + column_count;
596 }
597 new_columns.push(col.clone());
598 }
599 worksheet.cols = new_columns;
602
603 Ok(())
604 }
605
606 pub fn delete_columns(
614 &mut self,
615 sheet: u32,
616 column: i32,
617 column_count: i32,
618 ) -> Result<(), String> {
619 if column_count <= 0 {
620 return Err("Please use insert columns instead".to_string());
621 }
622 if !(1..=LAST_COLUMN).contains(&column) {
623 return Err(format!("Column number '{column}' is not valid."));
624 }
625 if column + column_count - 1 > LAST_COLUMN {
626 return Err("Cannot delete columns beyond the last column of the sheet".to_string());
627 }
628 if !self.can_delete_columns(sheet, column, column_count)? {
629 return Err(
630 "Cannot delete columns because that would break an array formula".to_string(),
631 );
632 }
633
634 self.reset_dynamic_array_spills(sheet)?;
635 let column_start = column;
637 let column_end = column + column_count - 1;
639
640 let worksheet = &self.workbook.worksheet(sheet)?;
642 let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
643 all_rows.sort_unstable();
645
646 for r in all_rows {
647 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
648 for col in columns {
649 if col >= column_start {
650 if col > column_end {
651 self.move_cell(sheet, r, col, r, col - column_count)?;
652 } else {
653 self.workbook.worksheet_mut(sheet)?.remove_cell(r, col)?;
654 }
655 }
656 }
657 }
658 displace_links(self.workbook.worksheet_mut(sheet)?, |r, c| {
660 if c < column_start {
661 Some((r, c))
662 } else if c <= column_end {
663 None
664 } else {
665 Some((r, c - column_count))
666 }
667 });
668
669 let disp = DisplaceData::Column {
671 sheet,
672 column,
673 delta: -column_count,
674 };
675 self.displace_cells(&disp)?;
676 self.displace_cf_ranges(sheet, &disp);
677 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
678
679 let mut new_columns = Vec::new();
681 for col in worksheet.cols.iter_mut() {
682 let min = col.min;
684 let max = col.max;
685 if column_start < min {
697 if column_end < min {
698 let mut new_column = col.clone();
701 new_column.min = min - column_count;
702 new_column.max = max - column_count;
703 new_columns.push(new_column);
704 } else if column_end < max {
705 let mut new_column = col.clone();
708 new_column.min = column_start;
709 new_column.max = max - column_count;
710 new_columns.push(new_column);
711 } else {
712 }
715 } else if column_start <= max {
716 if column_end <= max {
717 let mut new_column = col.clone();
720 new_column.max = max - column_count;
721 new_columns.push(new_column);
722 } else {
723 let mut new_column = col.clone();
725 new_column.max = column_start - 1;
726 new_columns.push(new_column);
727 }
728 } else {
729 new_columns.push(col.clone());
732 }
733 }
734 worksheet.cols = new_columns;
735
736 Ok(())
737 }
738
739 fn can_insert_rows(&self, sheet: u32, row: i32, _row_count: i32) -> Result<bool, String> {
744 let cell_coords: Vec<(i32, i32)> = {
745 let worksheet = self.workbook.worksheet(sheet)?;
746 worksheet
747 .sheet_data
748 .iter()
749 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
750 .collect()
751 };
752 for (r, c) in cell_coords {
753 if let CellStructure::ArrayFormula { range: (_, height) } =
754 self.get_cell_structure(sheet, r, c)?
755 {
756 if r < row && row < r + height {
760 return Ok(false);
761 }
762 }
763 }
764 Ok(true)
765 }
766
767 fn can_insert_columns(
769 &self,
770 sheet: u32,
771 column: i32,
772 _column_count: i32,
773 ) -> Result<bool, String> {
774 let cell_coords: Vec<(i32, i32)> = {
775 let worksheet = self.workbook.worksheet(sheet)?;
776 worksheet
777 .sheet_data
778 .iter()
779 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
780 .collect()
781 };
782 for (r, c) in cell_coords {
783 if let CellStructure::ArrayFormula { range: (width, _) } =
784 self.get_cell_structure(sheet, r, c)?
785 {
786 if c < column && column < c + width {
787 return Ok(false);
788 }
789 }
790 }
791 Ok(true)
792 }
793
794 fn can_delete_rows(&self, sheet: u32, row: i32, row_count: i32) -> Result<bool, String> {
798 let row_end = row + row_count; let cell_coords: Vec<(i32, i32)> = {
800 let worksheet = self.workbook.worksheet(sheet)?;
801 worksheet
802 .sheet_data
803 .iter()
804 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
805 .collect()
806 };
807 for (r, c) in cell_coords {
808 if let CellStructure::ArrayFormula { range: (_, height) } =
809 self.get_cell_structure(sheet, r, c)?
810 {
811 let overlaps = r < row_end && r + height > row;
813 let contained = r >= row && r + height <= row_end;
814 if overlaps && !contained {
815 return Ok(false);
816 }
817 }
818 }
819 Ok(true)
820 }
821
822 fn can_delete_columns(
825 &self,
826 sheet: u32,
827 column: i32,
828 column_count: i32,
829 ) -> Result<bool, String> {
830 let col_end = column + column_count; let cell_coords: Vec<(i32, i32)> = {
832 let worksheet = self.workbook.worksheet(sheet)?;
833 worksheet
834 .sheet_data
835 .iter()
836 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
837 .collect()
838 };
839 for (r, c) in cell_coords {
840 if let CellStructure::ArrayFormula { range: (width, _) } =
841 self.get_cell_structure(sheet, r, c)?
842 {
843 let overlaps = c < col_end && c + width > column;
845 let contained = c >= column && c + width <= col_end;
846 if overlaps && !contained {
847 return Ok(false);
848 }
849 }
850 }
851 Ok(true)
852 }
853
854 pub fn insert_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
862 if row_count <= 0 {
863 return Err("Cannot add a negative number of cells :)".to_string());
864 }
865 if !self.can_insert_rows(sheet, row, row_count)? {
866 return Err("Cannot insert rows because that would break an array formula".to_string());
867 }
868 let dimensions = self.workbook.worksheet(sheet)?.dimension();
870 let last_row = dimensions.max_row + row_count;
871 if last_row > LAST_ROW {
872 return Err(
873 "Cannot shift cells because that would delete cells at the end of a column"
874 .to_string(),
875 );
876 }
877
878 self.reset_dynamic_array_spills(sheet)?;
879 let worksheet = &self.workbook.worksheet(sheet)?;
881 let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
882 all_rows.sort_unstable();
883 all_rows.reverse();
884 for r in all_rows {
885 if r >= row {
886 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
888 for column in columns {
889 self.move_cell(sheet, r, column, r + row_count, column)?;
890 }
891 } else {
892 break;
894 }
895 }
896 let rows = &self.workbook.worksheets[sheet as usize].rows;
900 let mut new_rows = vec![];
901 for r in rows {
902 if r.r < row {
903 new_rows.push(r.clone());
904 } else if r.r >= row {
905 let mut new_row = r.clone();
906 new_row.r = r.r + row_count;
907 new_rows.push(new_row);
908 }
909 }
910 self.workbook.worksheets[sheet as usize].rows = new_rows;
911
912 displace_links(self.workbook.worksheet_mut(sheet)?, |r, c| {
914 if r >= row {
915 Some((r + row_count, c))
916 } else {
917 Some((r, c))
918 }
919 });
920
921 let disp = DisplaceData::Row {
923 sheet,
924 row,
925 delta: row_count,
926 };
927 self.displace_cells(&disp)?;
928 self.displace_cf_ranges(sheet, &disp);
929
930 Ok(())
931 }
932
933 pub fn delete_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
941 if row_count <= 0 {
942 return Err("Please use insert rows instead".to_string());
943 }
944 if !(1..=LAST_ROW).contains(&row) {
945 return Err(format!("Row number '{row}' is not valid."));
946 }
947 if row + row_count - 1 > LAST_ROW {
948 return Err("Cannot delete rows beyond the last row of the sheet".to_string());
949 }
950 if !self.can_delete_rows(sheet, row, row_count)? {
951 return Err("Cannot delete rows because that would break an array formula".to_string());
952 }
953
954 self.reset_dynamic_array_spills(sheet)?;
955 let worksheet = &self.workbook.worksheet(sheet)?;
957 let mut all_rows: Vec<i32> = worksheet.sheet_data.keys().copied().collect();
958 all_rows.sort_unstable();
959
960 for r in all_rows {
961 if r >= row {
962 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
964 if r >= row + row_count {
965 for column in columns {
967 self.move_cell(sheet, r, column, r - row_count, column)?;
968 }
969 } else {
970 self.workbook.worksheet_mut(sheet)?.sheet_data.remove(&r);
972 }
973 }
974 }
975 let rows = &self.workbook.worksheets[sheet as usize].rows;
980 let mut new_rows = vec![];
981 for r in rows {
982 if r.r < row {
983 new_rows.push(r.clone());
984 } else if r.r >= row + row_count {
985 let mut new_row = r.clone();
986 new_row.r = r.r - row_count;
987 new_rows.push(new_row);
988 }
989 }
990 self.workbook.worksheets[sheet as usize].rows = new_rows;
991
992 displace_links(self.workbook.worksheet_mut(sheet)?, |r, c| {
994 if r < row {
995 Some((r, c))
996 } else if r < row + row_count {
997 None
998 } else {
999 Some((r - row_count, c))
1000 }
1001 });
1002
1003 let disp = DisplaceData::Row {
1004 sheet,
1005 row,
1006 delta: -row_count,
1007 };
1008 self.displace_cells(&disp)?;
1009 self.displace_cf_ranges(sheet, &disp);
1010 Ok(())
1011 }
1012
1013 fn move_column_unchecked(&mut self, sheet: u32, column: i32, delta: i32) -> Result<(), String> {
1016 let target_column = column + delta;
1017
1018 let worksheet = self.workbook.worksheet_mut(sheet)?;
1023 let moved_links: Vec<(i32, Link)> = worksheet
1024 .links
1025 .iter()
1026 .filter(|(&(_, c), _)| c == column)
1027 .map(|(&(r, _), link)| (r, link.clone()))
1028 .collect();
1029 displace_links(worksheet, |r, c| {
1030 if c == column {
1031 None
1032 } else if delta > 0 && c > column && c <= target_column {
1033 Some((r, c - 1))
1034 } else if delta < 0 && c >= target_column && c < column {
1035 Some((r, c + 1))
1036 } else {
1037 Some((r, c))
1038 }
1039 });
1040
1041 let original_refs = self
1042 .workbook
1043 .worksheet(sheet)?
1044 .column_cell_references(column)?;
1045 let mut original_cells = Vec::new();
1046 for r in &original_refs {
1047 let cell = self
1048 .workbook
1049 .worksheet(sheet)?
1050 .cell(r.row, column)
1051 .ok_or("Expected Cell to exist")?;
1052 let style_idx = cell.get_style();
1053 let formula_or_value =
1054 self.get_cell_formula(sheet, r.row, column)?
1055 .unwrap_or_else(|| {
1056 cell.get_localized_text(
1057 &self.workbook.shared_strings,
1058 self.locale,
1059 self.language,
1060 )
1061 });
1062
1063 let mut array = None;
1064
1065 match cell {
1066 Cell::EmptyCell { .. }
1067 | Cell::BooleanCell { .. }
1068 | Cell::NumberCell { .. }
1069 | Cell::ErrorCell { .. }
1070 | Cell::SharedString { .. }
1071 | Cell::CellFormula { .. } => {
1072 }
1074 Cell::SpillCell { .. } => {
1075 let worksheet = self.workbook.worksheet_mut(sheet)?;
1078 worksheet.remove_cell(r.row, column)?;
1079 continue;
1080 }
1081 Cell::ArrayFormula {
1082 r,
1083 kind: ArrayKind::Dynamic,
1084 ..
1085 } => {
1086 debug_assert_eq!(*r, (1, 1));
1089 }
1090 Cell::ArrayFormula {
1091 r,
1092 kind: ArrayKind::Cse,
1093 ..
1094 } => {
1095 array = Some(*r);
1098 }
1099 }
1100
1101 original_cells.push((r.row, formula_or_value, style_idx, array));
1102 let ws = self.workbook.worksheet_mut(sheet)?;
1103 ws.remove_cell(r.row, column)?;
1104 }
1105 let width = self
1106 .workbook
1107 .worksheet(sheet)?
1108 .get_actual_column_width(column)?;
1109 let style = self.workbook.worksheet(sheet)?.get_column_style(column)?;
1110 let hidden = self.workbook.worksheet(sheet)?.is_column_hidden(column)?;
1111 if delta > 0 {
1112 for c in column + 1..=target_column {
1113 let refs = self.workbook.worksheet(sheet)?.column_cell_references(c)?;
1114 for r in refs {
1115 self.move_cell(sheet, r.row, c, r.row, c - 1)?;
1116 }
1117 let w = self.workbook.worksheet(sheet)?.get_actual_column_width(c)?;
1118 let s = self.workbook.worksheet(sheet)?.get_column_style(c)?;
1119 let h = self.workbook.worksheet(sheet)?.is_column_hidden(c)?;
1120 self.workbook
1121 .worksheet_mut(sheet)?
1122 .set_column_width_and_style(c - 1, w, h, s)?;
1123 }
1124 } else {
1125 for c in (target_column..=column - 1).rev() {
1126 let refs = self.workbook.worksheet(sheet)?.column_cell_references(c)?;
1127 for r in refs {
1128 self.move_cell(sheet, r.row, c, r.row, c + 1)?;
1129 }
1130 let w = self.workbook.worksheet(sheet)?.get_actual_column_width(c)?;
1131 let s = self.workbook.worksheet(sheet)?.get_column_style(c)?;
1132 let h = self.workbook.worksheet(sheet)?.is_column_hidden(c)?;
1133 self.workbook
1134 .worksheet_mut(sheet)?
1135 .set_column_width_and_style(c + 1, w, h, s)?;
1136 }
1137 }
1138 for (r, value, style_idx, array) in original_cells {
1139 if let Some(a) = array {
1140 self.set_user_array_formula(sheet, r, target_column, a.0, a.1, &value)?;
1141 } else {
1142 self.set_user_input(sheet, r, target_column, value)?;
1143 }
1144 self.workbook
1145 .worksheet_mut(sheet)?
1146 .set_cell_style(r, target_column, style_idx)?;
1147 }
1148 self.workbook
1149 .worksheet_mut(sheet)?
1150 .set_column_width_and_style(target_column, width, hidden, style)?;
1151
1152 let worksheet = self.workbook.worksheet_mut(sheet)?;
1154 worksheet.links.retain(|&(_, c), _| c != target_column);
1155 for (r, link) in moved_links {
1156 worksheet.links.insert((r, target_column), link);
1157 }
1158
1159 let disp = DisplaceData::ColumnMove {
1160 sheet,
1161 column,
1162 delta,
1163 };
1164 self.displace_cells(&disp)?;
1165 self.displace_cf_ranges(sheet, &disp);
1166 Ok(())
1167 }
1168
1169 fn move_row_unchecked(&mut self, sheet: u32, row: i32, delta: i32) -> Result<(), String> {
1171 let target_row = row + delta;
1172
1173 let worksheet = self.workbook.worksheet_mut(sheet)?;
1178 let moved_links: Vec<(i32, Link)> = worksheet
1179 .links
1180 .iter()
1181 .filter(|(&(r, _), _)| r == row)
1182 .map(|(&(_, c), link)| (c, link.clone()))
1183 .collect();
1184 displace_links(worksheet, |r, c| {
1185 if r == row {
1186 None
1187 } else if delta > 0 && r > row && r <= target_row {
1188 Some((r - 1, c))
1189 } else if delta < 0 && r >= target_row && r < row {
1190 Some((r + 1, c))
1191 } else {
1192 Some((r, c))
1193 }
1194 });
1195
1196 let original_cols = self.get_columns_for_row(sheet, row, false)?;
1197 let mut original_cells = Vec::new();
1198 for c in &original_cols {
1199 let cell = self
1200 .workbook
1201 .worksheet(sheet)?
1202 .cell(row, *c)
1203 .ok_or("Expected Cell to exist")?;
1204 let style_idx = cell.get_style();
1205 let formula_or_value = self.get_cell_formula(sheet, row, *c)?.unwrap_or_else(|| {
1206 cell.get_localized_text(&self.workbook.shared_strings, self.locale, self.language)
1207 });
1208 let mut array = None;
1209
1210 match cell {
1211 Cell::EmptyCell { .. }
1212 | Cell::BooleanCell { .. }
1213 | Cell::NumberCell { .. }
1214 | Cell::ErrorCell { .. }
1215 | Cell::SharedString { .. }
1216 | Cell::CellFormula { .. } => {
1217 }
1219 Cell::SpillCell { .. } => {
1220 let worksheet = self.workbook.worksheet_mut(sheet)?;
1223 worksheet.remove_cell(row, *c)?;
1224 continue;
1225 }
1226 Cell::ArrayFormula {
1227 r,
1228 kind: ArrayKind::Dynamic,
1229 ..
1230 } => {
1231 debug_assert_eq!(*r, (1, 1));
1234 }
1235 Cell::ArrayFormula {
1236 r,
1237 kind: ArrayKind::Cse,
1238 ..
1239 } => {
1240 array = Some(*r);
1243 }
1244 }
1245 original_cells.push((*c, formula_or_value, style_idx, array));
1246 let ws = self.workbook.worksheet_mut(sheet)?;
1247 ws.remove_cell(row, *c)?;
1248 }
1249 if delta > 0 {
1250 for r in row + 1..=target_row {
1251 let cols = self.get_columns_for_row(sheet, r, false)?;
1252 for c in cols {
1253 self.move_cell(sheet, r, c, r - 1, c)?;
1254 }
1255 }
1256 } else {
1257 for r in (target_row..=row - 1).rev() {
1258 let cols = self.get_columns_for_row(sheet, r, false)?;
1259 for c in cols {
1260 self.move_cell(sheet, r, c, r + 1, c)?;
1261 }
1262 }
1263 }
1264 for (c, value, style_idx, array) in original_cells {
1265 if let Some(array_range) = array {
1266 self.set_user_array_formula(
1267 sheet,
1268 target_row,
1269 c,
1270 array_range.0,
1271 array_range.1,
1272 &value,
1273 )?;
1274 } else {
1275 self.set_user_input(sheet, target_row, c, value)?;
1276 }
1277 self.workbook
1278 .worksheet_mut(sheet)?
1279 .set_cell_style(target_row, c, style_idx)?;
1280 }
1281 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
1282 let mut new_rows = Vec::new();
1283 for r in worksheet.rows.iter() {
1284 if r.r == row {
1285 let mut nr = r.clone();
1286 nr.r = target_row;
1287 new_rows.push(nr);
1288 } else if delta > 0 && r.r > row && r.r <= target_row {
1289 let mut nr = r.clone();
1290 nr.r -= 1;
1291 new_rows.push(nr);
1292 } else if delta < 0 && r.r < row && r.r >= target_row {
1293 let mut nr = r.clone();
1294 nr.r += 1;
1295 new_rows.push(nr);
1296 } else {
1297 new_rows.push(r.clone());
1298 }
1299 }
1300 worksheet.rows = new_rows;
1301
1302 let worksheet = self.workbook.worksheet_mut(sheet)?;
1304 worksheet.links.retain(|&(r, _), _| r != target_row);
1305 for (c, link) in moved_links {
1306 worksheet.links.insert((target_row, c), link);
1307 }
1308
1309 let disp = DisplaceData::RowMove { sheet, row, delta };
1310 self.displace_cells(&disp)?;
1311 self.displace_cf_ranges(sheet, &disp);
1312 Ok(())
1313 }
1314
1315 fn can_move_columns_action(
1319 &self,
1320 sheet: u32,
1321 column: i32,
1322 column_count: i32,
1323 delta: i32,
1324 ) -> Result<bool, String> {
1325 if delta == 0 {
1326 return Ok(true);
1327 }
1328
1329 let group_start = column;
1330 let group_end = column + column_count - 1;
1331
1332 let (displace_start, displace_end) = if delta > 0 {
1333 (group_end + 1, group_end + delta)
1334 } else {
1335 (group_start + delta, group_start - 1)
1336 };
1337
1338 let overlaps = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1339 a_start <= b_end && b_start <= a_end
1340 };
1341
1342 let contains = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1343 a_start <= b_start && b_end <= a_end
1344 };
1345
1346 let interval_is_safe = |array_start: i32, array_end: i32| {
1347 let safe_for = |start: i32, end: i32| {
1348 !overlaps(start, end, array_start, array_end)
1349 || contains(start, end, array_start, array_end)
1350 };
1351 safe_for(group_start, group_end) && safe_for(displace_start, displace_end)
1352 };
1353
1354 let cell_coords: Vec<(i32, i32)> = {
1355 let worksheet = self.workbook.worksheet(sheet)?;
1356 worksheet
1357 .sheet_data
1358 .iter()
1359 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
1360 .collect()
1361 };
1362
1363 for (r, c) in cell_coords {
1364 match self.get_cell_structure(sheet, r, c)? {
1365 CellStructure::ArrayFormula { range } => {
1366 let (width, _) = range;
1367 let array_start_col = c;
1368 let array_end_col = c + width - 1;
1369
1370 if !interval_is_safe(array_start_col, array_end_col) {
1371 return Ok(false);
1372 }
1373 }
1374 CellStructure::SpillArray { anchor, range } => {
1375 let (width, _) = range;
1376 let (_, array_start_col) = anchor;
1377 let array_end_col = array_start_col + width - 1;
1378
1379 if !interval_is_safe(array_start_col, array_end_col) {
1380 return Ok(false);
1381 }
1382 }
1383 _ => {}
1384 }
1385 }
1386
1387 Ok(true)
1388 }
1389
1390 fn can_move_rows_action(
1396 &self,
1397 sheet: u32,
1398 row: i32,
1399 row_count: i32,
1400 delta: i32,
1401 ) -> Result<bool, String> {
1402 if delta == 0 {
1403 return Ok(true);
1404 }
1405
1406 let group_start = row;
1407 let group_end = row + row_count - 1;
1408
1409 let (displace_start, displace_end) = if delta > 0 {
1410 (group_end + 1, group_end + delta)
1411 } else {
1412 (group_start + delta, group_start - 1)
1413 };
1414
1415 let overlaps = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1416 a_start <= b_end && b_start <= a_end
1417 };
1418
1419 let contains = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1420 a_start <= b_start && b_end <= a_end
1421 };
1422
1423 let interval_is_safe = |array_start: i32, array_end: i32| {
1424 let safe_for = |start: i32, end: i32| {
1425 !overlaps(start, end, array_start, array_end)
1426 || contains(start, end, array_start, array_end)
1427 };
1428
1429 safe_for(group_start, group_end) && safe_for(displace_start, displace_end)
1430 };
1431
1432 let cell_coords: Vec<(i32, i32)> = {
1434 let worksheet = self.workbook.worksheet(sheet)?;
1435 worksheet
1436 .sheet_data
1437 .iter()
1438 .flat_map(|(r, row_data)| row_data.keys().map(move |c| (*r, *c)))
1439 .collect()
1440 };
1441
1442 for (r, c) in cell_coords {
1443 match self.get_cell_structure(sheet, r, c)? {
1444 CellStructure::ArrayFormula { range } => {
1445 let (_, height) = range;
1446 let array_start_row = r;
1447 let array_end_row = r + height - 1;
1448
1449 if !interval_is_safe(array_start_row, array_end_row) {
1450 return Ok(false);
1451 }
1452 }
1453 CellStructure::SpillArray { anchor, range } => {
1454 let (_, height) = range;
1455 let (array_start_row, _) = anchor;
1456 let array_end_row = array_start_row + height - 1;
1457
1458 if !interval_is_safe(array_start_row, array_end_row) {
1459 return Ok(false);
1460 }
1461 }
1462 _ => {}
1463 }
1464 }
1465
1466 Ok(true)
1467 }
1468
1469 pub fn move_columns_action(
1484 &mut self,
1485 sheet: u32,
1486 column: i32,
1487 column_count: i32,
1488 delta: i32,
1489 ) -> Result<(), String> {
1490 if column_count <= 0 || delta == 0 {
1491 return Ok(());
1492 }
1493 let target_first = column + delta;
1494 let target_last = column + column_count - 1 + delta;
1495 if !(1..=LAST_COLUMN).contains(&target_first) || !(1..=LAST_COLUMN).contains(&target_last) {
1496 return Err("Target column out of boundaries".to_string());
1497 }
1498 if !(1..=LAST_COLUMN).contains(&column)
1499 || !(1..=LAST_COLUMN).contains(&(column + column_count - 1))
1500 {
1501 return Err("Initial column out of boundaries".to_string());
1502 }
1503 if !self.can_move_columns_action(sheet, column, column_count, delta)? {
1504 return Err(
1505 "Cannot move columns because that would split an array formula".to_string(),
1506 );
1507 }
1508 self.reset_dynamic_array_spills(sheet)?;
1509
1510 if delta > 0 {
1512 for col in (column..column + column_count).rev() {
1513 self.move_column_unchecked(sheet, col, delta)?;
1514 }
1515 } else {
1516 for col in column..column + column_count {
1517 self.move_column_unchecked(sheet, col, delta)?;
1518 }
1519 }
1520
1521 Ok(())
1522 }
1523
1524 pub fn move_rows_action(
1531 &mut self,
1532 sheet: u32,
1533 row: i32,
1534 row_count: i32,
1535 delta: i32,
1536 ) -> Result<(), String> {
1537 if row_count <= 0 || delta == 0 {
1538 return Ok(());
1539 }
1540 let target_first = row + delta;
1541 let target_last = row + row_count - 1 + delta;
1542 if !(1..=LAST_ROW).contains(&target_first) || !(1..=LAST_ROW).contains(&target_last) {
1543 return Err("Target row out of boundaries".to_string());
1544 }
1545 if !(1..=LAST_ROW).contains(&row) || !(1..=LAST_ROW).contains(&(row + row_count - 1)) {
1546 return Err("Initial row out of boundaries".to_string());
1547 }
1548 if !self.can_move_rows_action(sheet, row, row_count, delta)? {
1549 return Err("Cannot move rows because that would split an array formula".to_string());
1550 }
1551 self.reset_dynamic_array_spills(sheet)?;
1552
1553 if delta > 0 {
1555 for r in (row..row + row_count).rev() {
1556 self.move_row_unchecked(sheet, r, delta)?;
1557 }
1558 } else {
1559 for r in row..row + row_count {
1560 self.move_row_unchecked(sheet, r, delta)?;
1561 }
1562 }
1563 Ok(())
1564 }
1565}