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, MergedCell, 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_merged_cells<F>(worksheet: &mut Worksheet, map: F)
34where
35 F: Fn(i32, i32, i32, i32) -> Option<(i32, i32, i32, i32)>,
36{
37 let merged_cells = std::mem::take(&mut worksheet.merged_cells);
38 worksheet.merged_cells = merged_cells
39 .into_iter()
40 .filter_map(|m| {
41 map(m.row, m.column, m.last_row(), m.last_column()).map(
42 |(first_row, first_column, last_row, last_column)| MergedCell {
43 row: first_row,
44 column: first_column,
45 width: last_column - first_column + 1,
46 height: last_row - first_row + 1,
47 },
48 )
49 })
50 .filter(|m| m.width >= 1 && m.height >= 1 && !(m.width == 1 && m.height == 1))
51 .collect();
52}
53
54fn interval_survives_move(
60 start: i32,
61 end: i32,
62 group_start: i32,
63 group_end: i32,
64 delta: i32,
65) -> bool {
66 let (displace_start, displace_end) = if delta > 0 {
67 (group_end + 1, group_end + delta)
68 } else {
69 (group_start + delta, group_start - 1)
70 };
71 let inside = |a: i32, b: i32| start >= a && end <= b;
72 let outside = |a: i32, b: i32| end < a || start > b;
73 (inside(group_start, group_end) || outside(group_start, group_end))
74 && (inside(displace_start, displace_end) || outside(displace_start, displace_end))
75}
76
77fn displace_interval_for_move(
81 start: i32,
82 end: i32,
83 group_start: i32,
84 group_end: i32,
85 delta: i32,
86 count: i32,
87) -> (i32, i32) {
88 if start >= group_start && end <= group_end {
89 (start + delta, end + delta)
90 } else if delta > 0 && start > group_end && end <= group_end + delta {
91 (start - count, end - count)
92 } else if delta < 0 && start >= group_start + delta && end < group_start {
93 (start + count, end + count)
94 } else {
95 (start, end)
96 }
97}
98
99fn displace_cf_row(row: i32, data: &DisplaceData, sheet: u32) -> Option<i32> {
101 match data {
102 DisplaceData::Row {
103 sheet: s,
104 row: dr,
105 delta,
106 } if *s == sheet => {
107 if row >= *dr {
108 if *delta < 0 && row < *dr - *delta {
109 None
110 } else {
111 Some(row + *delta)
112 }
113 } else {
114 Some(row)
115 }
116 }
117 DisplaceData::RowMove {
118 sheet: s,
119 row: mr,
120 delta,
121 } if *s == sheet => {
122 if row == *mr {
123 Some(row + *delta)
124 } else if *delta > 0 && row > *mr && row <= *mr + *delta {
125 Some(row - 1)
126 } else if *delta < 0 && row < *mr && row >= *mr + *delta {
127 Some(row + 1)
128 } else {
129 Some(row)
130 }
131 }
132 _ => Some(row),
133 }
134}
135
136fn displace_cf_col(col: i32, data: &DisplaceData, sheet: u32) -> Option<i32> {
138 match data {
139 DisplaceData::Column {
140 sheet: s,
141 column: dc,
142 delta,
143 } if *s == sheet => {
144 if col >= *dc {
145 if *delta < 0 && col < *dc - *delta {
146 None
147 } else {
148 Some(col + *delta)
149 }
150 } else {
151 Some(col)
152 }
153 }
154 DisplaceData::ColumnMove {
155 sheet: s,
156 column: mc,
157 delta,
158 } if *s == sheet => {
159 if col == *mc {
160 Some(col + *delta)
161 } else if *delta > 0 && col > *mc && col <= *mc + *delta {
162 Some(col - 1)
163 } else if *delta < 0 && col < *mc && col >= *mc + *delta {
164 Some(col + 1)
165 } else {
166 Some(col)
167 }
168 }
169 _ => Some(col),
170 }
171}
172
173fn displace_cf_sqref_part(part: &str, data: &DisplaceData, sheet: u32) -> String {
176 let upper = part.to_uppercase();
177 let segs: Vec<&str> = upper.splitn(2, ':').collect();
178 match segs.len() {
179 1 => {
180 if let Some(r) = utils::parse_reference_a1(segs[0]) {
181 if let (Some(nr), Some(nc)) = (
182 displace_cf_row(r.row, data, sheet),
183 displace_cf_col(r.column, data, sheet),
184 ) {
185 if let Some(c) = utils::number_to_column(nc) {
186 return format!("{c}{nr}");
187 }
188 }
189 }
190 part.to_string()
191 }
192 2 => {
193 if let (Some(r1), Some(r2)) = (
194 utils::parse_reference_a1(segs[0]),
195 utils::parse_reference_a1(segs[1]),
196 ) {
197 if let (Some(nr1), Some(nc1), Some(nr2), Some(nc2)) = (
198 displace_cf_row(r1.row, data, sheet),
199 displace_cf_col(r1.column, data, sheet),
200 displace_cf_row(r2.row, data, sheet),
201 displace_cf_col(r2.column, data, sheet),
202 ) {
203 if let (Some(c1), Some(c2)) =
204 (utils::number_to_column(nc1), utils::number_to_column(nc2))
205 {
206 return format!("{c1}{nr1}:{c2}{nr2}");
207 }
208 }
209 }
210 part.to_string()
211 }
212 _ => part.to_string(),
213 }
214}
215
216fn displace_cf_sqref(sqref: &str, data: &DisplaceData, sheet: u32) -> String {
218 sqref
219 .split_whitespace()
220 .map(|p| displace_cf_sqref_part(p, data, sheet))
221 .collect::<Vec<_>>()
222 .join(" ")
223}
224
225fn displace_cf_formula_str(
234 parser: &mut ExprParser<'_>,
235 formula: &str,
236 context: &CellReferenceRC,
237 data: &DisplaceData,
238) -> String {
239 let trimmed = formula.trim();
240 let has_eq = trimmed.starts_with('=');
241 let body = if has_eq { &trimmed[1..] } else { trimmed };
242 let node = parser.parse(body, context);
243 let displaced = to_string_displaced(
244 &node,
245 context,
246 data,
247 get_default_locale(),
248 get_default_language(),
249 );
250 if has_eq {
251 format!("={displaced}")
252 } else {
253 displaced
254 }
255}
256
257fn displace_cfvo(
258 parser: &mut ExprParser<'_>,
259 cfvo: Cfvo,
260 context: &CellReferenceRC,
261 data: &DisplaceData,
262) -> Cfvo {
263 if let Cfvo::Formula(f) = cfvo {
264 Cfvo::Formula(displace_cf_formula_str(parser, &f, context, data))
265 } else {
266 cfvo
267 }
268}
269
270fn displace_cf_rule_formulas(
272 parser: &mut ExprParser<'_>,
273 rule: CfRule,
274 context: &CellReferenceRC,
275 data: &DisplaceData,
276) -> CfRule {
277 match rule {
278 CfRule::Formula {
279 formula,
280 dxf_id,
281 stop_if_true,
282 } => CfRule::Formula {
283 formula: displace_cf_formula_str(parser, &formula, context, data),
284 dxf_id,
285 stop_if_true,
286 },
287 CfRule::CellIs {
288 operator,
289 formula,
290 formula2,
291 dxf_id,
292 stop_if_true,
293 } => CfRule::CellIs {
294 operator,
295 formula: displace_cf_formula_str(parser, &formula, context, data),
296 formula2: formula2.map(|f| displace_cf_formula_str(parser, &f, context, data)),
297 dxf_id,
298 stop_if_true,
299 },
300 CfRule::ColorScale { thresholds } => CfRule::ColorScale {
301 thresholds: thresholds
302 .into_iter()
303 .map(|mut t| {
304 t.cfvo = displace_cfvo(parser, t.cfvo, context, data);
305 t
306 })
307 .collect(),
308 },
309 CfRule::DataBar {
310 min,
311 max,
312 positive_color,
313 negative_color,
314 is_gradient,
315 show_value,
316 } => CfRule::DataBar {
317 min: min.map(|c| displace_cfvo(parser, c, context, data)),
318 max: max.map(|c| displace_cfvo(parser, c, context, data)),
319 positive_color,
320 negative_color,
321 is_gradient,
322 show_value,
323 },
324 CfRule::IconSet {
325 thresholds,
326 show_value,
327 } => CfRule::IconSet {
328 thresholds: thresholds
329 .into_iter()
330 .map(|mut t| {
331 t.cfvo = displace_cfvo(parser, t.cfvo, context, data);
332 t
333 })
334 .collect(),
335 show_value,
336 },
337 CfRule::IconRating {
338 icon,
339 color,
340 thresholds,
341 show_value,
342 } => CfRule::IconRating {
343 icon,
344 color,
345 thresholds: thresholds
346 .into_iter()
347 .map(|(cfvo, strict)| (displace_cfvo(parser, cfvo, context, data), strict))
348 .collect(),
349 show_value,
350 },
351 other => other,
353 }
354}
355
356impl<'a> Model<'a> {
357 fn shift_cell_formula(
358 &mut self,
359 sheet: u32,
360 row: i32,
361 column: i32,
362 displace_data: &DisplaceData,
363 ) -> Result<(), String> {
364 if let Some(f) = self
365 .workbook
366 .worksheet(sheet)?
367 .cell(row, column)
368 .and_then(|c| c.get_formula())
369 {
370 let node = &self.parsed_formulas[sheet as usize][f as usize].0.clone();
371 let cell_reference = CellReferenceRC {
372 sheet: self.workbook.worksheets[sheet as usize].get_name(),
373 row,
374 column,
375 };
376 let formula = to_localized_string(node, &cell_reference, self.locale, self.language);
381 let formula_displaced = to_string_displaced(
382 node,
383 &cell_reference,
384 displace_data,
385 self.locale,
386 self.language,
387 );
388 if formula != formula_displaced {
389 self.update_cell_with_formula(sheet, row, column, format!("={formula_displaced}"))?;
390 };
391 }
392 Ok(())
393 }
394 fn displace_cells(&mut self, displace_data: &DisplaceData) -> Result<(), String> {
400 let cells = self.get_all_cells();
401 for cell in cells {
402 self.shift_cell_formula(cell.index, cell.row, cell.column, displace_data)?;
403 }
404 Ok(())
405 }
406
407 fn displace_cf_ranges(&mut self, sheet: u32, displace_data: &DisplaceData) {
409 let count = match self.workbook.worksheets.get(sheet as usize) {
410 Some(ws) => ws.conditional_formatting.len(),
411 None => return,
412 };
413
414 let sheet_name = self.workbook.worksheets[sheet as usize].get_name();
416 let mut phase1: Vec<(usize, String, CfRule, i32, i32)> = Vec::with_capacity(count);
417 for idx in 0..count {
418 let cf = &self.workbook.worksheets[sheet as usize].conditional_formatting[idx];
419 let old_range = cf.range.clone();
420 let new_range = displace_cf_sqref(&old_range, displace_data, sheet);
421 let rule = cf.cf_rule.clone();
422 if let Some((anchor_row, anchor_col)) = cf_sqref_anchor(&old_range) {
423 phase1.push((idx, new_range, rule, anchor_row, anchor_col));
424 }
425 }
426
427 let locale = self.locale;
431 let language = self.language;
432 self.parser.set_locale(get_default_locale());
433 self.parser.set_language(get_default_language());
434 for (idx, new_range, rule, anchor_row, anchor_col) in phase1 {
435 let context = CellReferenceRC {
436 sheet: sheet_name.clone(),
437 row: anchor_row,
438 column: anchor_col,
439 };
440 let new_rule =
441 displace_cf_rule_formulas(&mut self.parser, rule, &context, displace_data);
442 self.workbook.worksheets[sheet as usize].conditional_formatting[idx].range = new_range;
443 self.workbook.worksheets[sheet as usize].conditional_formatting[idx].cf_rule = new_rule;
444 }
445 self.parser.set_locale(locale);
446 self.parser.set_language(language);
447 }
448
449 fn get_columns_for_row(
463 &self,
464 sheet: u32,
465 row: i32,
466 descending: bool,
467 ) -> Result<Vec<i32>, String> {
468 let worksheet = self.workbook.worksheet(sheet)?;
469 let mut columns = worksheet.sheet_data.columns_in_row(row);
470 if descending {
471 columns.reverse();
472 }
473 Ok(columns)
474 }
475
476 fn move_cell(
489 &mut self,
490 sheet: u32,
491 source_row: i32,
492 source_column: i32,
493 target_row: i32,
494 target_column: i32,
495 ) -> Result<(), String> {
496 let source_cell = match self
497 .workbook
498 .worksheet(sheet)?
499 .cell(source_row, source_column)
500 {
501 Some(c) => c.into_owned(),
502 None => return Ok(()),
503 };
504 let style = source_cell.get_style();
505
506 let mut array = None;
507
508 match &source_cell {
509 Cell::EmptyCell { .. }
510 | Cell::BooleanCell { .. }
511 | Cell::NumberCell { .. }
512 | Cell::ErrorCell { .. }
513 | Cell::SharedString { .. }
514 | Cell::CellFormula { .. } => {
515 }
517 Cell::SpillCell { a, .. } => {
518 let worksheet = self.workbook.worksheet_mut(sheet)?;
524 let live_cse = matches!(
525 worksheet.cell(a.0, a.1).as_deref(),
526 Some(Cell::ArrayFormula { r: (width, height), kind: ArrayKind::Cse, .. })
527 if source_row >= a.0
528 && source_row < a.0 + height
529 && source_column >= a.1
530 && source_column < a.1 + width
531 );
532 if !live_cse {
533 worksheet.remove_cell(source_row, source_column)?;
534 }
535 return Ok(());
536 }
537 Cell::ArrayFormula {
538 r,
539 kind: ArrayKind::Dynamic,
540 ..
541 } => {
542 debug_assert_eq!(*r, (1, 1));
545 }
546 Cell::ArrayFormula {
547 r,
548 kind: ArrayKind::Cse,
549 ..
550 } => {
551 array = Some(*r);
554 }
555 }
556 let formula_or_value = self
557 .get_cell_formula(sheet, source_row, source_column)?
558 .unwrap_or_else(|| {
559 source_cell.get_localized_text(
560 &self.workbook.shared_strings,
561 self.locale,
562 self.language,
563 )
564 });
565
566 if let Some((width, height)) = array {
567 let worksheet = self.workbook.worksheet_mut(sheet)?;
571 for r in source_row..source_row + height {
572 for c in source_column..source_column + width {
573 if worksheet.cell(r, c).is_some() {
574 worksheet.remove_cell(r, c)?;
575 }
576 }
577 }
578 self.set_user_array_formula(
579 sheet,
580 target_row,
581 target_column,
582 width,
583 height,
584 &formula_or_value,
585 )?;
586 } else {
587 self.set_user_input(sheet, target_row, target_column, formula_or_value)?;
588 }
589
590 let worksheet = self.workbook.worksheet_mut(sheet)?;
591 worksheet.set_cell_style(target_row, target_column, style)?;
593
594 if array.is_none() {
598 worksheet.remove_cell(source_row, source_column)?;
599 }
600 Ok(())
601 }
602
603 pub fn insert_columns(
613 &mut self,
614 sheet: u32,
615 column: i32,
616 column_count: i32,
617 ) -> Result<(), String> {
618 if column_count <= 0 {
619 return Err("Cannot add a negative number of cells :)".to_string());
620 }
621 if !self.can_insert_columns(sheet, column, column_count)? {
622 return Err(
623 "Cannot insert columns because that would break an array formula".to_string(),
624 );
625 }
626 let dimensions = self.workbook.worksheet(sheet)?.dimension();
628 let last_column = dimensions.max_column + column_count;
629 if last_column > LAST_COLUMN {
630 return Err(
631 "Cannot shift cells because that would delete cells at the end of a row"
632 .to_string(),
633 );
634 }
635 self.reset_dynamic_array_spills(sheet)?;
636 let merged_cells = std::mem::take(&mut self.workbook.worksheet_mut(sheet)?.merged_cells);
639 let worksheet = self.workbook.worksheet(sheet)?;
640 let all_rows = worksheet.sheet_data.rows();
641 for row in all_rows {
642 let sorted_columns = self.get_columns_for_row(sheet, row, true)?;
643 for col in sorted_columns {
644 if col >= column {
645 self.move_cell(sheet, row, col, row, col + column_count)?;
646 } else {
647 break;
649 }
650 }
651 }
652
653 displace_links(self.workbook.worksheet_mut(sheet)?, |r, c| {
655 if c >= column {
656 Some((r, c + column_count))
657 } else {
658 Some((r, c))
659 }
660 });
661
662 self.workbook.worksheet_mut(sheet)?.merged_cells = merged_cells;
664 displace_merged_cells(
665 self.workbook.worksheet_mut(sheet)?,
666 |first_row, first_column, last_row, last_column| {
667 let first = if first_column >= column {
668 first_column + column_count
669 } else {
670 first_column
671 };
672 let last = if last_column >= column {
673 last_column + column_count
674 } else {
675 last_column
676 };
677 if first > LAST_COLUMN {
678 return None;
679 }
680 Some((first_row, first, last_row, last.min(LAST_COLUMN)))
681 },
682 );
683
684 let disp = DisplaceData::Column {
686 sheet,
687 column,
688 delta: column_count,
689 };
690 self.displace_cells(&disp)?;
691 self.displace_cf_ranges(sheet, &disp);
692
693 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
698
699 let mut new_columns = Vec::new();
700 for col in worksheet.cols.iter_mut() {
701 let min = col.min;
703 let max = col.max;
704 if column > max {
705 } else if column <= min {
707 col.min = min + column_count;
709 col.max = max + column_count;
710 } else {
711 col.max = max + column_count;
713 }
714 new_columns.push(col.clone());
715 }
716 worksheet.cols = new_columns;
719
720 Ok(())
721 }
722
723 pub fn delete_columns(
731 &mut self,
732 sheet: u32,
733 column: i32,
734 column_count: i32,
735 ) -> Result<(), String> {
736 if column_count <= 0 {
737 return Err("Please use insert columns instead".to_string());
738 }
739 if !(1..=LAST_COLUMN).contains(&column) {
740 return Err(format!("Column number '{column}' is not valid."));
741 }
742 if column + column_count - 1 > LAST_COLUMN {
743 return Err("Cannot delete columns beyond the last column of the sheet".to_string());
744 }
745 if !self.can_delete_columns(sheet, column, column_count)? {
746 return Err(
747 "Cannot delete columns because that would break an array formula".to_string(),
748 );
749 }
750
751 self.reset_dynamic_array_spills(sheet)?;
752 let merged_cells = std::mem::take(&mut self.workbook.worksheet_mut(sheet)?.merged_cells);
755 let column_start = column;
757 let column_end = column + column_count - 1;
759
760 let worksheet = &self.workbook.worksheet(sheet)?;
762 let all_rows = worksheet.sheet_data.rows();
763
764 for r in all_rows {
765 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
766 for col in columns {
767 if col >= column_start {
768 if col > column_end {
769 self.move_cell(sheet, r, col, r, col - column_count)?;
770 } else {
771 self.workbook.worksheet_mut(sheet)?.remove_cell(r, col)?;
772 }
773 }
774 }
775 }
776 displace_links(self.workbook.worksheet_mut(sheet)?, |r, c| {
778 if c < column_start {
779 Some((r, c))
780 } else if c <= column_end {
781 None
782 } else {
783 Some((r, c - column_count))
784 }
785 });
786
787 self.workbook.worksheet_mut(sheet)?.merged_cells = merged_cells;
789 displace_merged_cells(
790 self.workbook.worksheet_mut(sheet)?,
791 |first_row, first_column, last_row, last_column| {
792 let first = if first_column < column_start {
793 first_column
794 } else if first_column <= column_end {
795 column_start
796 } else {
797 first_column - column_count
798 };
799 let last = if last_column < column_start {
800 last_column
801 } else if last_column <= column_end {
802 column_start - 1
803 } else {
804 last_column - column_count
805 };
806 if last < first {
807 return None;
808 }
809 Some((first_row, first, last_row, last))
810 },
811 );
812
813 let disp = DisplaceData::Column {
815 sheet,
816 column,
817 delta: -column_count,
818 };
819 self.displace_cells(&disp)?;
820 self.displace_cf_ranges(sheet, &disp);
821 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
822
823 let mut new_columns = Vec::new();
825 for col in worksheet.cols.iter_mut() {
826 let min = col.min;
828 let max = col.max;
829 if column_start < min {
841 if column_end < min {
842 let mut new_column = col.clone();
845 new_column.min = min - column_count;
846 new_column.max = max - column_count;
847 new_columns.push(new_column);
848 } else if column_end < max {
849 let mut new_column = col.clone();
852 new_column.min = column_start;
853 new_column.max = max - column_count;
854 new_columns.push(new_column);
855 } else {
856 }
859 } else if column_start <= max {
860 if column_end <= max {
861 let mut new_column = col.clone();
864 new_column.max = max - column_count;
865 new_columns.push(new_column);
866 } else {
867 let mut new_column = col.clone();
869 new_column.max = column_start - 1;
870 new_columns.push(new_column);
871 }
872 } else {
873 new_columns.push(col.clone());
876 }
877 }
878 worksheet.cols = new_columns;
879
880 Ok(())
881 }
882
883 fn can_insert_rows(&self, sheet: u32, row: i32, _row_count: i32) -> Result<bool, String> {
888 let cell_coords: Vec<(i32, i32)> = {
889 let worksheet = self.workbook.worksheet(sheet)?;
890 worksheet
891 .sheet_data
892 .cells()
893 .map(|(r, c, _)| (r, c))
894 .collect()
895 };
896 for (r, c) in cell_coords {
897 if let CellStructure::ArrayFormula { range: (_, height) } =
898 self.get_cell_structure(sheet, r, c)?
899 {
900 if r < row && row < r + height {
904 return Ok(false);
905 }
906 }
907 }
908 Ok(true)
909 }
910
911 fn can_insert_columns(
913 &self,
914 sheet: u32,
915 column: i32,
916 _column_count: i32,
917 ) -> Result<bool, String> {
918 let cell_coords: Vec<(i32, i32)> = {
919 let worksheet = self.workbook.worksheet(sheet)?;
920 worksheet
921 .sheet_data
922 .cells()
923 .map(|(r, c, _)| (r, c))
924 .collect()
925 };
926 for (r, c) in cell_coords {
927 if let CellStructure::ArrayFormula { range: (width, _) } =
928 self.get_cell_structure(sheet, r, c)?
929 {
930 if c < column && column < c + width {
931 return Ok(false);
932 }
933 }
934 }
935 Ok(true)
936 }
937
938 fn can_delete_rows(&self, sheet: u32, row: i32, row_count: i32) -> Result<bool, String> {
942 let row_end = row + row_count; let cell_coords: Vec<(i32, i32)> = {
944 let worksheet = self.workbook.worksheet(sheet)?;
945 worksheet
946 .sheet_data
947 .cells()
948 .map(|(r, c, _)| (r, c))
949 .collect()
950 };
951 for (r, c) in cell_coords {
952 if let CellStructure::ArrayFormula { range: (_, height) } =
953 self.get_cell_structure(sheet, r, c)?
954 {
955 let overlaps = r < row_end && r + height > row;
957 let contained = r >= row && r + height <= row_end;
958 if overlaps && !contained {
959 return Ok(false);
960 }
961 }
962 }
963 Ok(true)
964 }
965
966 fn can_delete_columns(
969 &self,
970 sheet: u32,
971 column: i32,
972 column_count: i32,
973 ) -> Result<bool, String> {
974 let col_end = column + column_count; let cell_coords: Vec<(i32, i32)> = {
976 let worksheet = self.workbook.worksheet(sheet)?;
977 worksheet
978 .sheet_data
979 .cells()
980 .map(|(r, c, _)| (r, c))
981 .collect()
982 };
983 for (r, c) in cell_coords {
984 if let CellStructure::ArrayFormula { range: (width, _) } =
985 self.get_cell_structure(sheet, r, c)?
986 {
987 let overlaps = c < col_end && c + width > column;
989 let contained = c >= column && c + width <= col_end;
990 if overlaps && !contained {
991 return Ok(false);
992 }
993 }
994 }
995 Ok(true)
996 }
997
998 pub fn insert_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
1006 if row_count <= 0 {
1007 return Err("Cannot add a negative number of cells :)".to_string());
1008 }
1009 if !self.can_insert_rows(sheet, row, row_count)? {
1010 return Err("Cannot insert rows because that would break an array formula".to_string());
1011 }
1012 let dimensions = self.workbook.worksheet(sheet)?.dimension();
1014 let last_row = dimensions.max_row + row_count;
1015 if last_row > LAST_ROW {
1016 return Err(
1017 "Cannot shift cells because that would delete cells at the end of a column"
1018 .to_string(),
1019 );
1020 }
1021
1022 self.reset_dynamic_array_spills(sheet)?;
1023 let merged_cells = std::mem::take(&mut self.workbook.worksheet_mut(sheet)?.merged_cells);
1026 let worksheet = &self.workbook.worksheet(sheet)?;
1028 let mut all_rows = worksheet.sheet_data.rows();
1029 all_rows.reverse();
1030 for r in all_rows {
1031 if r >= row {
1032 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
1034 for column in columns {
1035 self.move_cell(sheet, r, column, r + row_count, column)?;
1036 }
1037 } else {
1038 break;
1040 }
1041 }
1042 let rows = &self.workbook.worksheets[sheet as usize].rows;
1046 let mut new_rows = vec![];
1047 for r in rows {
1048 if r.r < row {
1049 new_rows.push(r.clone());
1050 } else if r.r >= row {
1051 let mut new_row = r.clone();
1052 new_row.r = r.r + row_count;
1053 new_rows.push(new_row);
1054 }
1055 }
1056 self.workbook.worksheets[sheet as usize].rows = new_rows;
1057
1058 displace_links(self.workbook.worksheet_mut(sheet)?, |r, c| {
1060 if r >= row {
1061 Some((r + row_count, c))
1062 } else {
1063 Some((r, c))
1064 }
1065 });
1066
1067 self.workbook.worksheet_mut(sheet)?.merged_cells = merged_cells;
1069 displace_merged_cells(
1070 self.workbook.worksheet_mut(sheet)?,
1071 |first_row, first_column, last_row, last_column| {
1072 let first = if first_row >= row {
1073 first_row + row_count
1074 } else {
1075 first_row
1076 };
1077 let last = if last_row >= row {
1078 last_row + row_count
1079 } else {
1080 last_row
1081 };
1082 if first > LAST_ROW {
1083 return None;
1084 }
1085 Some((first, first_column, last.min(LAST_ROW), last_column))
1086 },
1087 );
1088
1089 let disp = DisplaceData::Row {
1091 sheet,
1092 row,
1093 delta: row_count,
1094 };
1095 self.displace_cells(&disp)?;
1096 self.displace_cf_ranges(sheet, &disp);
1097
1098 Ok(())
1099 }
1100
1101 pub fn delete_rows(&mut self, sheet: u32, row: i32, row_count: i32) -> Result<(), String> {
1109 if row_count <= 0 {
1110 return Err("Please use insert rows instead".to_string());
1111 }
1112 if !(1..=LAST_ROW).contains(&row) {
1113 return Err(format!("Row number '{row}' is not valid."));
1114 }
1115 if row + row_count - 1 > LAST_ROW {
1116 return Err("Cannot delete rows beyond the last row of the sheet".to_string());
1117 }
1118 if !self.can_delete_rows(sheet, row, row_count)? {
1119 return Err("Cannot delete rows because that would break an array formula".to_string());
1120 }
1121
1122 self.reset_dynamic_array_spills(sheet)?;
1123 let merged_cells = std::mem::take(&mut self.workbook.worksheet_mut(sheet)?.merged_cells);
1126 let worksheet = &self.workbook.worksheet(sheet)?;
1128 let all_rows = worksheet.sheet_data.rows();
1129
1130 for r in all_rows {
1131 if r >= row {
1132 let columns: Vec<i32> = self.get_columns_for_row(sheet, r, false)?;
1134 if r >= row + row_count {
1135 for column in columns {
1137 self.move_cell(sheet, r, column, r - row_count, column)?;
1138 }
1139 } else {
1140 self.workbook.worksheet_mut(sheet)?.sheet_data.remove_row(r);
1142 }
1143 }
1144 }
1145 let rows = &self.workbook.worksheets[sheet as usize].rows;
1150 let mut new_rows = vec![];
1151 for r in rows {
1152 if r.r < row {
1153 new_rows.push(r.clone());
1154 } else if r.r >= row + row_count {
1155 let mut new_row = r.clone();
1156 new_row.r = r.r - row_count;
1157 new_rows.push(new_row);
1158 }
1159 }
1160 self.workbook.worksheets[sheet as usize].rows = new_rows;
1161
1162 displace_links(self.workbook.worksheet_mut(sheet)?, |r, c| {
1164 if r < row {
1165 Some((r, c))
1166 } else if r < row + row_count {
1167 None
1168 } else {
1169 Some((r - row_count, c))
1170 }
1171 });
1172
1173 self.workbook.worksheet_mut(sheet)?.merged_cells = merged_cells;
1175 displace_merged_cells(
1176 self.workbook.worksheet_mut(sheet)?,
1177 |first_row, first_column, last_row, last_column| {
1178 let first = if first_row < row {
1179 first_row
1180 } else if first_row < row + row_count {
1181 row
1182 } else {
1183 first_row - row_count
1184 };
1185 let last = if last_row < row {
1186 last_row
1187 } else if last_row < row + row_count {
1188 row - 1
1189 } else {
1190 last_row - row_count
1191 };
1192 if last < first {
1193 return None;
1194 }
1195 Some((first, first_column, last, last_column))
1196 },
1197 );
1198
1199 let disp = DisplaceData::Row {
1200 sheet,
1201 row,
1202 delta: -row_count,
1203 };
1204 self.displace_cells(&disp)?;
1205 self.displace_cf_ranges(sheet, &disp);
1206 Ok(())
1207 }
1208
1209 fn move_column_unchecked(&mut self, sheet: u32, column: i32, delta: i32) -> Result<(), String> {
1212 let target_column = column + delta;
1213
1214 let worksheet = self.workbook.worksheet_mut(sheet)?;
1219 let moved_links: Vec<(i32, Link)> = worksheet
1220 .links
1221 .iter()
1222 .filter(|(&(_, c), _)| c == column)
1223 .map(|(&(r, _), link)| (r, link.clone()))
1224 .collect();
1225 displace_links(worksheet, |r, c| {
1226 if c == column {
1227 None
1228 } else if delta > 0 && c > column && c <= target_column {
1229 Some((r, c - 1))
1230 } else if delta < 0 && c >= target_column && c < column {
1231 Some((r, c + 1))
1232 } else {
1233 Some((r, c))
1234 }
1235 });
1236
1237 let original_refs = self
1238 .workbook
1239 .worksheet(sheet)?
1240 .column_cell_references(column)?;
1241 let mut original_cells = Vec::new();
1242 for r in &original_refs {
1243 let cell = self
1244 .workbook
1245 .worksheet(sheet)?
1246 .cell(r.row, column)
1247 .ok_or("Expected Cell to exist")?
1248 .into_owned();
1249 let style_idx = cell.get_style();
1250 let formula_or_value =
1251 self.get_cell_formula(sheet, r.row, column)?
1252 .unwrap_or_else(|| {
1253 cell.get_localized_text(
1254 &self.workbook.shared_strings,
1255 self.locale,
1256 self.language,
1257 )
1258 });
1259
1260 let mut array = None;
1261
1262 match &cell {
1263 Cell::EmptyCell { .. }
1264 | Cell::BooleanCell { .. }
1265 | Cell::NumberCell { .. }
1266 | Cell::ErrorCell { .. }
1267 | Cell::SharedString { .. }
1268 | Cell::CellFormula { .. } => {
1269 }
1271 Cell::SpillCell { .. } => {
1272 let worksheet = self.workbook.worksheet_mut(sheet)?;
1275 worksheet.remove_cell(r.row, column)?;
1276 continue;
1277 }
1278 Cell::ArrayFormula {
1279 r,
1280 kind: ArrayKind::Dynamic,
1281 ..
1282 } => {
1283 debug_assert_eq!(*r, (1, 1));
1286 }
1287 Cell::ArrayFormula {
1288 r,
1289 kind: ArrayKind::Cse,
1290 ..
1291 } => {
1292 array = Some(*r);
1295 }
1296 }
1297
1298 original_cells.push((r.row, formula_or_value, style_idx, array));
1299 let ws = self.workbook.worksheet_mut(sheet)?;
1300 ws.remove_cell(r.row, column)?;
1301 }
1302 let width = self
1303 .workbook
1304 .worksheet(sheet)?
1305 .get_actual_column_width(column)?;
1306 let style = self.workbook.worksheet(sheet)?.get_column_style(column)?;
1307 let hidden = self.workbook.worksheet(sheet)?.is_column_hidden(column)?;
1308 if delta > 0 {
1309 for c in column + 1..=target_column {
1310 let refs = self.workbook.worksheet(sheet)?.column_cell_references(c)?;
1311 for r in refs {
1312 self.move_cell(sheet, r.row, c, r.row, c - 1)?;
1313 }
1314 let w = self.workbook.worksheet(sheet)?.get_actual_column_width(c)?;
1315 let s = self.workbook.worksheet(sheet)?.get_column_style(c)?;
1316 let h = self.workbook.worksheet(sheet)?.is_column_hidden(c)?;
1317 self.workbook
1318 .worksheet_mut(sheet)?
1319 .set_column_width_and_style(c - 1, w, h, s)?;
1320 }
1321 } else {
1322 for c in (target_column..=column - 1).rev() {
1323 let refs = self.workbook.worksheet(sheet)?.column_cell_references(c)?;
1324 for r in refs {
1325 self.move_cell(sheet, r.row, c, r.row, c + 1)?;
1326 }
1327 let w = self.workbook.worksheet(sheet)?.get_actual_column_width(c)?;
1328 let s = self.workbook.worksheet(sheet)?.get_column_style(c)?;
1329 let h = self.workbook.worksheet(sheet)?.is_column_hidden(c)?;
1330 self.workbook
1331 .worksheet_mut(sheet)?
1332 .set_column_width_and_style(c + 1, w, h, s)?;
1333 }
1334 }
1335 for (r, value, style_idx, array) in original_cells {
1336 if let Some(a) = array {
1337 self.set_user_array_formula(sheet, r, target_column, a.0, a.1, &value)?;
1338 } else {
1339 self.set_user_input(sheet, r, target_column, value)?;
1340 }
1341 self.workbook
1342 .worksheet_mut(sheet)?
1343 .set_cell_style(r, target_column, style_idx)?;
1344 }
1345 self.workbook
1346 .worksheet_mut(sheet)?
1347 .set_column_width_and_style(target_column, width, hidden, style)?;
1348
1349 let worksheet = self.workbook.worksheet_mut(sheet)?;
1351 worksheet.links.retain(|&(_, c), _| c != target_column);
1352 for (r, link) in moved_links {
1353 worksheet.links.insert((r, target_column), link);
1354 }
1355
1356 let disp = DisplaceData::ColumnMove {
1357 sheet,
1358 column,
1359 delta,
1360 };
1361 self.displace_cells(&disp)?;
1362 self.displace_cf_ranges(sheet, &disp);
1363 Ok(())
1364 }
1365
1366 fn move_row_unchecked(&mut self, sheet: u32, row: i32, delta: i32) -> Result<(), String> {
1368 let target_row = row + delta;
1369
1370 let worksheet = self.workbook.worksheet_mut(sheet)?;
1375 let moved_links: Vec<(i32, Link)> = worksheet
1376 .links
1377 .iter()
1378 .filter(|(&(r, _), _)| r == row)
1379 .map(|(&(_, c), link)| (c, link.clone()))
1380 .collect();
1381 displace_links(worksheet, |r, c| {
1382 if r == row {
1383 None
1384 } else if delta > 0 && r > row && r <= target_row {
1385 Some((r - 1, c))
1386 } else if delta < 0 && r >= target_row && r < row {
1387 Some((r + 1, c))
1388 } else {
1389 Some((r, c))
1390 }
1391 });
1392
1393 let original_cols = self.get_columns_for_row(sheet, row, false)?;
1394 let mut original_cells = Vec::new();
1395 for c in &original_cols {
1396 let cell = self
1397 .workbook
1398 .worksheet(sheet)?
1399 .cell(row, *c)
1400 .ok_or("Expected Cell to exist")?
1401 .into_owned();
1402 let style_idx = cell.get_style();
1403 let formula_or_value = self.get_cell_formula(sheet, row, *c)?.unwrap_or_else(|| {
1404 cell.get_localized_text(&self.workbook.shared_strings, self.locale, self.language)
1405 });
1406 let mut array = None;
1407
1408 match &cell {
1409 Cell::EmptyCell { .. }
1410 | Cell::BooleanCell { .. }
1411 | Cell::NumberCell { .. }
1412 | Cell::ErrorCell { .. }
1413 | Cell::SharedString { .. }
1414 | Cell::CellFormula { .. } => {
1415 }
1417 Cell::SpillCell { .. } => {
1418 let worksheet = self.workbook.worksheet_mut(sheet)?;
1421 worksheet.remove_cell(row, *c)?;
1422 continue;
1423 }
1424 Cell::ArrayFormula {
1425 r,
1426 kind: ArrayKind::Dynamic,
1427 ..
1428 } => {
1429 debug_assert_eq!(*r, (1, 1));
1432 }
1433 Cell::ArrayFormula {
1434 r,
1435 kind: ArrayKind::Cse,
1436 ..
1437 } => {
1438 array = Some(*r);
1441 }
1442 }
1443 original_cells.push((*c, formula_or_value, style_idx, array));
1444 let ws = self.workbook.worksheet_mut(sheet)?;
1445 ws.remove_cell(row, *c)?;
1446 }
1447 if delta > 0 {
1448 for r in row + 1..=target_row {
1449 let cols = self.get_columns_for_row(sheet, r, false)?;
1450 for c in cols {
1451 self.move_cell(sheet, r, c, r - 1, c)?;
1452 }
1453 }
1454 } else {
1455 for r in (target_row..=row - 1).rev() {
1456 let cols = self.get_columns_for_row(sheet, r, false)?;
1457 for c in cols {
1458 self.move_cell(sheet, r, c, r + 1, c)?;
1459 }
1460 }
1461 }
1462 for (c, value, style_idx, array) in original_cells {
1463 if let Some(array_range) = array {
1464 self.set_user_array_formula(
1465 sheet,
1466 target_row,
1467 c,
1468 array_range.0,
1469 array_range.1,
1470 &value,
1471 )?;
1472 } else {
1473 self.set_user_input(sheet, target_row, c, value)?;
1474 }
1475 self.workbook
1476 .worksheet_mut(sheet)?
1477 .set_cell_style(target_row, c, style_idx)?;
1478 }
1479 let worksheet = &mut self.workbook.worksheet_mut(sheet)?;
1480 let mut new_rows = Vec::new();
1481 for r in worksheet.rows.iter() {
1482 if r.r == row {
1483 let mut nr = r.clone();
1484 nr.r = target_row;
1485 new_rows.push(nr);
1486 } else if delta > 0 && r.r > row && r.r <= target_row {
1487 let mut nr = r.clone();
1488 nr.r -= 1;
1489 new_rows.push(nr);
1490 } else if delta < 0 && r.r < row && r.r >= target_row {
1491 let mut nr = r.clone();
1492 nr.r += 1;
1493 new_rows.push(nr);
1494 } else {
1495 new_rows.push(r.clone());
1496 }
1497 }
1498 worksheet.rows = new_rows;
1499
1500 let worksheet = self.workbook.worksheet_mut(sheet)?;
1502 worksheet.links.retain(|&(r, _), _| r != target_row);
1503 for (c, link) in moved_links {
1504 worksheet.links.insert((target_row, c), link);
1505 }
1506
1507 let disp = DisplaceData::RowMove { sheet, row, delta };
1508 self.displace_cells(&disp)?;
1509 self.displace_cf_ranges(sheet, &disp);
1510 Ok(())
1511 }
1512
1513 fn can_move_columns_action(
1517 &self,
1518 sheet: u32,
1519 column: i32,
1520 column_count: i32,
1521 delta: i32,
1522 ) -> Result<bool, String> {
1523 if delta == 0 {
1524 return Ok(true);
1525 }
1526
1527 let group_start = column;
1528 let group_end = column + column_count - 1;
1529
1530 let (displace_start, displace_end) = if delta > 0 {
1531 (group_end + 1, group_end + delta)
1532 } else {
1533 (group_start + delta, group_start - 1)
1534 };
1535
1536 let overlaps = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1537 a_start <= b_end && b_start <= a_end
1538 };
1539
1540 let contains = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1541 a_start <= b_start && b_end <= a_end
1542 };
1543
1544 let interval_is_safe = |array_start: i32, array_end: i32| {
1545 let safe_for = |start: i32, end: i32| {
1546 !overlaps(start, end, array_start, array_end)
1547 || contains(start, end, array_start, array_end)
1548 };
1549 safe_for(group_start, group_end) && safe_for(displace_start, displace_end)
1550 };
1551
1552 let cell_coords: Vec<(i32, i32)> = {
1553 let worksheet = self.workbook.worksheet(sheet)?;
1554 worksheet
1555 .sheet_data
1556 .cells()
1557 .map(|(r, c, _)| (r, c))
1558 .collect()
1559 };
1560
1561 for (r, c) in cell_coords {
1562 match self.get_cell_structure(sheet, r, c)? {
1563 CellStructure::ArrayFormula { range } => {
1564 let (width, _) = range;
1565 let array_start_col = c;
1566 let array_end_col = c + width - 1;
1567
1568 if !interval_is_safe(array_start_col, array_end_col) {
1569 return Ok(false);
1570 }
1571 }
1572 CellStructure::SpillArray { anchor, range } => {
1573 let (width, _) = range;
1574 let (_, array_start_col) = anchor;
1575 let array_end_col = array_start_col + width - 1;
1576
1577 if !interval_is_safe(array_start_col, array_end_col) {
1578 return Ok(false);
1579 }
1580 }
1581 _ => {}
1582 }
1583 }
1584
1585 Ok(true)
1586 }
1587
1588 fn can_move_rows_action(
1594 &self,
1595 sheet: u32,
1596 row: i32,
1597 row_count: i32,
1598 delta: i32,
1599 ) -> Result<bool, String> {
1600 if delta == 0 {
1601 return Ok(true);
1602 }
1603
1604 let group_start = row;
1605 let group_end = row + row_count - 1;
1606
1607 let (displace_start, displace_end) = if delta > 0 {
1608 (group_end + 1, group_end + delta)
1609 } else {
1610 (group_start + delta, group_start - 1)
1611 };
1612
1613 let overlaps = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1614 a_start <= b_end && b_start <= a_end
1615 };
1616
1617 let contains = |a_start: i32, a_end: i32, b_start: i32, b_end: i32| {
1618 a_start <= b_start && b_end <= a_end
1619 };
1620
1621 let interval_is_safe = |array_start: i32, array_end: i32| {
1622 let safe_for = |start: i32, end: i32| {
1623 !overlaps(start, end, array_start, array_end)
1624 || contains(start, end, array_start, array_end)
1625 };
1626
1627 safe_for(group_start, group_end) && safe_for(displace_start, displace_end)
1628 };
1629
1630 let cell_coords: Vec<(i32, i32)> = {
1632 let worksheet = self.workbook.worksheet(sheet)?;
1633 worksheet
1634 .sheet_data
1635 .cells()
1636 .map(|(r, c, _)| (r, c))
1637 .collect()
1638 };
1639
1640 for (r, c) in cell_coords {
1641 match self.get_cell_structure(sheet, r, c)? {
1642 CellStructure::ArrayFormula { range } => {
1643 let (_, height) = range;
1644 let array_start_row = r;
1645 let array_end_row = r + height - 1;
1646
1647 if !interval_is_safe(array_start_row, array_end_row) {
1648 return Ok(false);
1649 }
1650 }
1651 CellStructure::SpillArray { anchor, range } => {
1652 let (_, height) = range;
1653 let (array_start_row, _) = anchor;
1654 let array_end_row = array_start_row + height - 1;
1655
1656 if !interval_is_safe(array_start_row, array_end_row) {
1657 return Ok(false);
1658 }
1659 }
1660 _ => {}
1661 }
1662 }
1663
1664 Ok(true)
1665 }
1666
1667 pub fn move_columns_action(
1682 &mut self,
1683 sheet: u32,
1684 column: i32,
1685 column_count: i32,
1686 delta: i32,
1687 ) -> Result<(), String> {
1688 if column_count <= 0 || delta == 0 {
1689 return Ok(());
1690 }
1691 let target_first = column + delta;
1692 let target_last = column + column_count - 1 + delta;
1693 if !(1..=LAST_COLUMN).contains(&target_first) || !(1..=LAST_COLUMN).contains(&target_last) {
1694 return Err("Target column out of boundaries".to_string());
1695 }
1696 if !(1..=LAST_COLUMN).contains(&column)
1697 || !(1..=LAST_COLUMN).contains(&(column + column_count - 1))
1698 {
1699 return Err("Initial column out of boundaries".to_string());
1700 }
1701 if !self.can_move_columns_action(sheet, column, column_count, delta)? {
1702 return Err(
1703 "Cannot move columns because that would split an array formula".to_string(),
1704 );
1705 }
1706 let group_end = column + column_count - 1;
1707 if self
1708 .workbook
1709 .worksheet(sheet)?
1710 .merged_cells
1711 .iter()
1712 .any(|m| !interval_survives_move(m.column, m.last_column(), column, group_end, delta))
1713 {
1714 return Err("Cannot move columns because that would split a merged cell".to_string());
1715 }
1716 self.reset_dynamic_array_spills(sheet)?;
1717
1718 let merged_cells = std::mem::take(&mut self.workbook.worksheet_mut(sheet)?.merged_cells);
1722
1723 if delta > 0 {
1725 for col in (column..column + column_count).rev() {
1726 self.move_column_unchecked(sheet, col, delta)?;
1727 }
1728 } else {
1729 for col in column..column + column_count {
1730 self.move_column_unchecked(sheet, col, delta)?;
1731 }
1732 }
1733
1734 self.workbook.worksheet_mut(sheet)?.merged_cells = merged_cells
1735 .into_iter()
1736 .map(|mut m| {
1737 let (first, last) = displace_interval_for_move(
1738 m.column,
1739 m.last_column(),
1740 column,
1741 group_end,
1742 delta,
1743 column_count,
1744 );
1745 m.column = first;
1746 m.width = last - first + 1;
1747 m
1748 })
1749 .collect();
1750
1751 Ok(())
1752 }
1753
1754 pub fn move_rows_action(
1761 &mut self,
1762 sheet: u32,
1763 row: i32,
1764 row_count: i32,
1765 delta: i32,
1766 ) -> Result<(), String> {
1767 if row_count <= 0 || delta == 0 {
1768 return Ok(());
1769 }
1770 let target_first = row + delta;
1771 let target_last = row + row_count - 1 + delta;
1772 if !(1..=LAST_ROW).contains(&target_first) || !(1..=LAST_ROW).contains(&target_last) {
1773 return Err("Target row out of boundaries".to_string());
1774 }
1775 if !(1..=LAST_ROW).contains(&row) || !(1..=LAST_ROW).contains(&(row + row_count - 1)) {
1776 return Err("Initial row out of boundaries".to_string());
1777 }
1778 if !self.can_move_rows_action(sheet, row, row_count, delta)? {
1779 return Err("Cannot move rows because that would split an array formula".to_string());
1780 }
1781 let group_end = row + row_count - 1;
1782 if self
1783 .workbook
1784 .worksheet(sheet)?
1785 .merged_cells
1786 .iter()
1787 .any(|m| !interval_survives_move(m.row, m.last_row(), row, group_end, delta))
1788 {
1789 return Err("Cannot move rows because that would split a merged cell".to_string());
1790 }
1791 self.reset_dynamic_array_spills(sheet)?;
1792
1793 let merged_cells = std::mem::take(&mut self.workbook.worksheet_mut(sheet)?.merged_cells);
1797
1798 if delta > 0 {
1800 for r in (row..row + row_count).rev() {
1801 self.move_row_unchecked(sheet, r, delta)?;
1802 }
1803 } else {
1804 for r in row..row + row_count {
1805 self.move_row_unchecked(sheet, r, delta)?;
1806 }
1807 }
1808
1809 self.workbook.worksheet_mut(sheet)?.merged_cells = merged_cells
1810 .into_iter()
1811 .map(|mut m| {
1812 let (first, last) = displace_interval_for_move(
1813 m.row,
1814 m.last_row(),
1815 row,
1816 group_end,
1817 delta,
1818 row_count,
1819 );
1820 m.row = first;
1821 m.height = last - first + 1;
1822 m
1823 })
1824 .collect();
1825
1826 Ok(())
1827 }
1828}