1use crate::actions::{
2 ActionCommand, ColumnAction, MultiColumnAction, MultiRowAction, RowAction, SheetAction,
3};
4use crate::app::AppState;
5use crate::utils::index_to_col_name;
6use anyhow::Result;
7
8impl AppState<'_> {
9 pub fn next_sheet(&mut self) -> Result<()> {
10 let sheet_count = self.workbook.get_sheet_names().len();
11 let current_index = self.workbook.get_current_sheet_index();
12
13 if current_index >= sheet_count - 1 {
14 self.add_notification("Already at the last sheet".to_string());
15 return Ok(());
16 }
17
18 self.switch_sheet_by_index(current_index + 1)
19 }
20
21 pub fn prev_sheet(&mut self) -> Result<()> {
22 let current_index = self.workbook.get_current_sheet_index();
23
24 if current_index == 0 {
25 self.add_notification("Already at the first sheet".to_string());
26 return Ok(());
27 }
28
29 self.switch_sheet_by_index(current_index - 1)
30 }
31
32 pub fn switch_sheet_by_index(&mut self, index: usize) -> Result<()> {
33 let current_sheet_name = self.workbook.get_current_sheet_name();
34
35 if !self.sheet_column_widths.contains_key(¤t_sheet_name)
36 || self.sheet_column_widths[¤t_sheet_name] != self.column_widths
37 {
38 self.sheet_column_widths
39 .insert(current_sheet_name, self.column_widths.clone());
40 }
41
42 self.selected_cell = (1, 1);
44 self.start_row = 1;
45 self.start_col = 1;
46
47 self.workbook.switch_sheet(index)?;
48
49 let new_sheet_name = self.workbook.get_current_sheet_name();
50
51 if let Some(saved_widths) = self.sheet_column_widths.get(&new_sheet_name) {
53 if &self.column_widths != saved_widths {
54 self.column_widths = saved_widths.clone();
55 }
56 } else {
57 let max_cols = self.workbook.get_current_sheet().max_cols;
58 let default_width = 15;
59 self.column_widths = vec![default_width; max_cols + 1];
60
61 self.sheet_column_widths
62 .insert(new_sheet_name.clone(), self.column_widths.clone());
63 }
64
65 if !self.search_results.is_empty() {
67 self.search_results.clear();
68 self.current_search_idx = None;
69 }
70
71 self.add_notification(format!("Switched to sheet: {}", new_sheet_name));
72 Ok(())
73 }
74
75 pub fn switch_to_sheet(&mut self, name_or_index: &str) {
76 let sheet_names = self.workbook.get_sheet_names();
78
79 if let Ok(index) = name_or_index.parse::<usize>() {
81 let zero_based_index = index.saturating_sub(1);
83
84 if zero_based_index < sheet_names.len() {
85 match self.switch_sheet_by_index(zero_based_index) {
86 Ok(_) => return,
87 Err(e) => {
88 self.add_notification(format!(
89 "Failed to switch to sheet {}: {}",
90 index, e
91 ));
92 return;
93 }
94 }
95 }
96 }
97
98 for (i, name) in sheet_names.iter().enumerate() {
100 if name.eq_ignore_ascii_case(name_or_index) {
101 match self.switch_sheet_by_index(i) {
102 Ok(_) => return,
103 Err(e) => {
104 self.add_notification(format!(
105 "Failed to switch to sheet '{}': {}",
106 name_or_index, e
107 ));
108 return;
109 }
110 }
111 }
112 }
113
114 self.add_notification(format!("Sheet '{}' not found", name_or_index));
116 }
117
118 pub fn delete_current_sheet(&mut self) {
119 let current_sheet_name = self.workbook.get_current_sheet_name();
120 let sheet_index = self.workbook.get_current_sheet_index();
121
122 let sheet_data = self.workbook.get_current_sheet().clone();
124 let column_widths = self.column_widths.clone();
125
126 match self.workbook.delete_current_sheet() {
127 Ok(_) => {
128 let sheet_action = SheetAction {
130 sheet_index,
131 sheet_name: current_sheet_name.clone(),
132 sheet_data,
133 column_widths,
134 };
135
136 self.undo_history.push(ActionCommand::Sheet(sheet_action));
137 self.sheet_column_widths.remove(¤t_sheet_name);
138
139 self.selected_cell = (1, 1);
141 self.start_row = 1;
142 self.start_col = 1;
143
144 let new_sheet_name = self.workbook.get_current_sheet_name();
145
146 if let Some(saved_widths) = self.sheet_column_widths.get(&new_sheet_name) {
147 self.column_widths = saved_widths.clone();
148 } else {
149 let max_cols = self.workbook.get_current_sheet().max_cols;
150 let default_width = 15;
151 self.column_widths = vec![default_width; max_cols + 1];
152
153 self.sheet_column_widths
154 .insert(new_sheet_name.clone(), self.column_widths.clone());
155 }
156
157 self.search_results.clear();
159 self.current_search_idx = None;
160
161 self.add_notification(format!("Deleted sheet: {}", current_sheet_name));
162 }
163 Err(e) => {
164 self.add_notification(format!("Failed to delete sheet: {}", e));
165 }
166 }
167 }
168
169 pub fn delete_current_row(&mut self) -> Result<()> {
170 let row = self.selected_cell.0;
171 let sheet = self.workbook.get_current_sheet();
172
173 if row < 1 || row > sheet.max_rows {
175 return Ok(());
176 }
177
178 let sheet_index = self.workbook.get_current_sheet_index();
180 let sheet_name = self.workbook.get_current_sheet_name();
181
182 let row_data = if row < sheet.data.len() {
184 sheet.data[row].clone()
185 } else {
186 Vec::new()
187 };
188
189 let row_action = RowAction {
191 sheet_index,
192 sheet_name,
193 row,
194 row_data,
195 };
196
197 self.undo_history.push(ActionCommand::Row(row_action));
198 self.workbook.delete_row(row)?;
199
200 self.workbook.recalculate_max_rows();
201 self.workbook.recalculate_max_cols();
202 let sheet = self.workbook.get_current_sheet();
203
204 if self.selected_cell.0 > sheet.max_rows {
205 self.selected_cell.0 = sheet.max_rows.max(1);
206 }
207
208 self.handle_scrolling();
209 self.search_results.clear();
210 self.current_search_idx = None;
211
212 self.add_notification(format!("Deleted row {}", row));
213 Ok(())
214 }
215
216 pub fn delete_row(&mut self, row: usize) -> Result<()> {
217 let sheet = self.workbook.get_current_sheet();
218
219 if row < 1 || row > sheet.max_rows {
221 return Ok(());
222 }
223
224 let sheet_index = self.workbook.get_current_sheet_index();
226 let sheet_name = self.workbook.get_current_sheet_name();
227
228 let row_data = if row < sheet.data.len() {
230 sheet.data[row].clone()
231 } else {
232 Vec::new()
233 };
234
235 let row_action = RowAction {
237 sheet_index,
238 sheet_name,
239 row,
240 row_data,
241 };
242
243 self.undo_history.push(ActionCommand::Row(row_action));
244 self.workbook.delete_row(row)?;
245
246 self.workbook.recalculate_max_rows();
247 self.workbook.recalculate_max_cols();
248 let sheet = self.workbook.get_current_sheet();
249
250 if self.selected_cell.0 > sheet.max_rows {
251 self.selected_cell.0 = sheet.max_rows.max(1);
252 }
253
254 self.handle_scrolling();
255 self.search_results.clear();
256 self.current_search_idx = None;
257
258 self.add_notification(format!("Deleted row {}", row));
259 Ok(())
260 }
261
262 pub fn delete_rows(&mut self, start_row: usize, end_row: usize) -> Result<()> {
263 if start_row == end_row {
264 return self.delete_row(start_row);
265 }
266
267 let sheet = self.workbook.get_current_sheet();
268
269 if start_row < 1 || start_row > sheet.max_rows || start_row > end_row {
271 return Ok(());
272 }
273
274 let effective_end_row = end_row.min(sheet.max_rows);
276
277 let sheet_index = self.workbook.get_current_sheet_index();
279 let sheet_name = self.workbook.get_current_sheet_name();
280
281 let rows_to_save = effective_end_row - start_row + 1;
283 let mut rows_data = Vec::with_capacity(rows_to_save);
284
285 for row in start_row..=effective_end_row {
286 if row < sheet.data.len() {
287 rows_data.push(sheet.data[row].clone());
288 } else {
289 rows_data.push(Vec::new());
290 }
291 }
292
293 let multi_row_action = MultiRowAction {
295 sheet_index,
296 sheet_name,
297 start_row,
298 end_row: effective_end_row,
299 rows_data,
300 };
301
302 self.undo_history
303 .push(ActionCommand::MultiRow(multi_row_action));
304 self.workbook.delete_rows(start_row, effective_end_row)?;
305
306 self.workbook.recalculate_max_rows();
307 self.workbook.recalculate_max_cols();
308 let sheet = self.workbook.get_current_sheet();
309
310 if self.selected_cell.0 > sheet.max_rows {
311 self.selected_cell.0 = sheet.max_rows.max(1);
312 }
313
314 self.handle_scrolling();
315 self.search_results.clear();
316 self.current_search_idx = None;
317
318 self.add_notification(format!(
319 "Deleted rows {} to {}",
320 start_row, effective_end_row
321 ));
322 Ok(())
323 }
324
325 pub fn delete_current_column(&mut self) -> Result<()> {
326 let col = self.selected_cell.1;
327 let sheet = self.workbook.get_current_sheet();
328
329 if col < 1 || col > sheet.max_cols {
331 return Ok(());
332 }
333
334 let sheet_index = self.workbook.get_current_sheet_index();
336 let sheet_name = self.workbook.get_current_sheet_name();
337
338 let mut column_data = Vec::with_capacity(sheet.data.len());
340 for row in &sheet.data {
341 if col < row.len() {
342 column_data.push(row[col].clone());
343 } else {
344 column_data.push(crate::excel::Cell::empty());
345 }
346 }
347
348 let column_width = if col < self.column_widths.len() {
350 self.column_widths[col]
351 } else {
352 15 };
354
355 let column_action = ColumnAction {
356 sheet_index,
357 sheet_name,
358 col,
359 column_data,
360 column_width,
361 };
362
363 self.undo_history.push(ActionCommand::Column(column_action));
364 self.workbook.delete_column(col)?;
365
366 self.workbook.recalculate_max_rows();
367 self.workbook.recalculate_max_cols();
368 let sheet = self.workbook.get_current_sheet();
369
370 if col > sheet.max_cols {
371 self.selected_cell.1 = sheet.max_cols.max(1);
372 }
373
374 if self.selected_cell.0 > sheet.max_rows {
375 self.selected_cell.0 = sheet.max_rows.max(1);
376 }
377
378 if self.column_widths.len() > col {
379 self.column_widths.remove(col);
380 }
381
382 self.adjust_column_widths(sheet.max_cols);
383
384 self.handle_scrolling();
385 self.search_results.clear();
386 self.current_search_idx = None;
387
388 self.add_notification(format!("Deleted column {}", index_to_col_name(col)));
389 Ok(())
390 }
391
392 pub fn delete_column(&mut self, col: usize) -> Result<()> {
393 let sheet = self.workbook.get_current_sheet();
394
395 if col < 1 || col > sheet.max_cols {
397 return Ok(());
398 }
399
400 let sheet_index = self.workbook.get_current_sheet_index();
402 let sheet_name = self.workbook.get_current_sheet_name();
403
404 let mut column_data = Vec::with_capacity(sheet.data.len());
406 for row in &sheet.data {
407 if col < row.len() {
408 column_data.push(row[col].clone());
409 } else {
410 column_data.push(crate::excel::Cell::empty());
411 }
412 }
413
414 let column_width = if col < self.column_widths.len() {
416 self.column_widths[col]
417 } else {
418 15 };
420
421 let column_action = ColumnAction {
422 sheet_index,
423 sheet_name,
424 col,
425 column_data,
426 column_width,
427 };
428
429 self.undo_history.push(ActionCommand::Column(column_action));
430 self.workbook.delete_column(col)?;
431
432 self.workbook.recalculate_max_rows();
433 self.workbook.recalculate_max_cols();
434 let sheet = self.workbook.get_current_sheet();
435
436 if self.selected_cell.1 > sheet.max_cols {
437 self.selected_cell.1 = sheet.max_cols.max(1);
438 }
439
440 if self.selected_cell.0 > sheet.max_rows {
441 self.selected_cell.0 = sheet.max_rows.max(1);
442 }
443
444 if self.column_widths.len() > col {
445 self.column_widths.remove(col);
446 }
447
448 self.adjust_column_widths(sheet.max_cols);
449
450 self.handle_scrolling();
451 self.search_results.clear();
452 self.current_search_idx = None;
453
454 self.add_notification(format!("Deleted column {}", index_to_col_name(col)));
455 Ok(())
456 }
457
458 pub fn delete_columns(&mut self, start_col: usize, end_col: usize) -> Result<()> {
459 if start_col == end_col {
460 return self.delete_column(start_col);
461 }
462
463 let sheet = self.workbook.get_current_sheet();
464
465 if start_col < 1 || start_col > sheet.max_cols || start_col > end_col {
467 return Ok(());
468 }
469
470 let effective_end_col = end_col.min(sheet.max_cols);
472
473 let sheet_index = self.workbook.get_current_sheet_index();
475 let sheet_name = self.workbook.get_current_sheet_name();
476
477 let cols_to_save = effective_end_col - start_col + 1;
479 let mut columns_data = Vec::with_capacity(cols_to_save);
480 let mut column_widths = Vec::with_capacity(cols_to_save);
481
482 for col in start_col..=effective_end_col {
483 let mut column_data = Vec::with_capacity(sheet.data.len());
485 for row in &sheet.data {
486 if col < row.len() {
487 column_data.push(row[col].clone());
488 } else {
489 column_data.push(crate::excel::Cell::empty());
490 }
491 }
492 columns_data.push(column_data);
493
494 let column_width = if col < self.column_widths.len() {
496 self.column_widths[col]
497 } else {
498 15 };
500 column_widths.push(column_width);
501 }
502
503 let multi_column_action = MultiColumnAction {
505 sheet_index,
506 sheet_name,
507 start_col,
508 end_col: effective_end_col,
509 columns_data,
510 column_widths,
511 };
512
513 self.undo_history
514 .push(ActionCommand::MultiColumn(multi_column_action));
515 self.workbook.delete_columns(start_col, effective_end_col)?;
516
517 self.workbook.recalculate_max_rows();
518 self.workbook.recalculate_max_cols();
519 let sheet = self.workbook.get_current_sheet();
520
521 if self.selected_cell.1 > sheet.max_cols {
522 self.selected_cell.1 = sheet.max_cols.max(1);
523 }
524
525 if self.selected_cell.0 > sheet.max_rows {
526 self.selected_cell.0 = sheet.max_rows.max(1);
527 }
528
529 for col in (start_col..=effective_end_col).rev() {
530 if self.column_widths.len() > col {
531 self.column_widths.remove(col);
532 }
533 }
534
535 self.adjust_column_widths(sheet.max_cols);
536
537 self.handle_scrolling();
538 self.search_results.clear();
539 self.current_search_idx = None;
540
541 self.add_notification(format!(
542 "Deleted columns {} to {}",
543 index_to_col_name(start_col),
544 index_to_col_name(effective_end_col)
545 ));
546 Ok(())
547 }
548
549 pub fn auto_adjust_column_width(&mut self, col: Option<usize>) {
550 let sheet = self.workbook.get_current_sheet();
551 let default_min_width = 5;
552
553 match col {
554 Some(column) => {
556 if column < self.column_widths.len() {
557 let width = self.calculate_column_width(column);
559 self.column_widths[column] = width.max(default_min_width);
560
561 self.ensure_column_visible(column);
562
563 self.add_notification(format!(
564 "Column {} width adjusted",
565 index_to_col_name(column)
566 ));
567 }
568 }
569 None => {
571 for col_idx in 1..=sheet.max_cols {
572 let width = self.calculate_column_width(col_idx);
573 self.column_widths[col_idx] = width.max(default_min_width);
574 }
575
576 let column = self.selected_cell.1;
577 self.ensure_column_visible(column);
578
579 self.add_notification("All column widths adjusted".to_string());
580 }
581 }
582 }
583
584 fn calculate_column_width(&self, col: usize) -> usize {
585 let sheet = self.workbook.get_current_sheet();
586
587 let col_name = index_to_col_name(col);
589 let mut max_width = 3.max(col_name.len());
590
591 for row in 1..=sheet.max_rows {
593 if row >= sheet.data.len() || col >= sheet.data[row].len() {
594 continue;
595 }
596
597 let content = &sheet.data[row][col].value;
598 if content.is_empty() {
599 continue;
600 }
601
602 let mut display_width = 0;
603
604 for c in content.chars() {
605 if c.is_ascii() {
606 display_width += 1;
607 } else {
608 display_width += 2;
609 }
610 }
611
612 max_width = max_width.max(display_width);
613 }
614 max_width
615 }
616
617 pub fn get_column_width(&self, col: usize) -> usize {
618 if col < self.column_widths.len() {
619 self.column_widths[col]
620 } else {
621 15 }
623 }
624
625 pub fn ensure_column_widths(&mut self) {
626 let sheet = self.workbook.get_current_sheet();
627 self.adjust_column_widths(sheet.max_cols);
628 }
629
630 fn adjust_column_widths(&mut self, max_cols: usize) {
631 match self.column_widths.len().cmp(&(max_cols + 1)) {
632 std::cmp::Ordering::Greater => {
633 self.column_widths.truncate(max_cols + 1);
634 }
635 std::cmp::Ordering::Less => {
636 let additional = max_cols + 1 - self.column_widths.len();
637 self.column_widths.extend(vec![15; additional]);
638 }
639 std::cmp::Ordering::Equal => {
640 }
642 }
643 }
644}