1#![deny(missing_docs)]
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 constants::{LAST_COLUMN, LAST_ROW},
7 expressions::utils::{is_valid_column_number, is_valid_row},
8 worksheet::NavigationDirection,
9};
10
11use super::common::UserModel;
12
13#[derive(Serialize, Deserialize)]
14#[cfg_attr(test, derive(PartialEq, Debug))]
15pub struct SelectedView {
16 pub sheet: u32,
17 pub row: i32,
18 pub column: i32,
19 pub range: [i32; 4],
20 pub top_row: i32,
21 pub left_column: i32,
22}
23
24impl<'a> UserModel<'a> {
25 fn ui_row_height(&self, sheet: u32, row: i32) -> Result<f64, String> {
31 self.model.get_row_height(sheet, row).map(f64::round)
32 }
33
34 fn ui_column_width(&self, sheet: u32, column: i32) -> Result<f64, String> {
35 self.model.get_column_width(sheet, column).map(f64::round)
36 }
37
38 fn merge_anchor(&self, sheet: u32, row: i32, column: i32) -> Result<(i32, i32), String> {
41 Ok(self
42 .model
43 .workbook
44 .worksheet(sheet)?
45 .merge_anchor(row, column))
46 }
47
48 fn single_cell_range(&self, sheet: u32, row: i32, column: i32) -> Result<[i32; 4], String> {
51 let worksheet = self.model.workbook.worksheet(sheet)?;
52 Ok(match worksheet.merged_cell_containing(row, column) {
53 Some(m) => [m.row, m.column, m.last_row(), m.last_column()],
54 None => [row, column, row, column],
55 })
56 }
57
58 fn selection_range(
65 &self,
66 sheet: u32,
67 anchor_row: i32,
68 anchor_column: i32,
69 focus_row: i32,
70 focus_column: i32,
71 ) -> Result<[i32; 4], String> {
72 let worksheet = self.model.workbook.worksheet(sheet)?;
73 let cell_rect = |row: i32, column: i32| match worksheet.merged_cell_containing(row, column)
74 {
75 Some(m) => (m.row, m.column, m.last_row(), m.last_column()),
76 None => (row, column, row, column),
77 };
78 let (anchor_first_row, anchor_first_column, anchor_last_row, anchor_last_column) =
79 cell_rect(anchor_row, anchor_column);
80 let (focus_first_row, focus_first_column, focus_last_row, focus_last_column) =
81 cell_rect(focus_row, focus_column);
82 self.grow_range_over_merged_cells(
83 sheet,
84 [
85 anchor_first_row.min(focus_first_row),
86 anchor_first_column.min(focus_first_column),
87 anchor_last_row.max(focus_last_row),
88 anchor_last_column.max(focus_last_column),
89 ],
90 )
91 }
92
93 fn grow_range_over_merged_cells(
98 &self,
99 sheet: u32,
100 range: [i32; 4],
101 ) -> Result<[i32; 4], String> {
102 let [start_row, start_column, end_row, end_column] = range;
103 let mut min_row = start_row.min(end_row);
104 let mut max_row = start_row.max(end_row);
105 let mut min_column = start_column.min(end_column);
106 let mut max_column = start_column.max(end_column);
107 let worksheet = self.model.workbook.worksheet(sheet)?;
108 loop {
109 let mut changed = false;
110 for m in &worksheet.merged_cells {
111 if m.intersects(
112 min_row,
113 min_column,
114 max_column - min_column + 1,
115 max_row - min_row + 1,
116 ) {
117 if m.row < min_row {
118 min_row = m.row;
119 changed = true;
120 }
121 if m.last_row() > max_row {
122 max_row = m.last_row();
123 changed = true;
124 }
125 if m.column < min_column {
126 min_column = m.column;
127 changed = true;
128 }
129 if m.last_column() > max_column {
130 max_column = m.last_column();
131 changed = true;
132 }
133 }
134 }
135 if !changed {
136 break;
137 }
138 }
139 let (new_start_row, new_end_row) = if start_row <= end_row {
140 (min_row, max_row)
141 } else {
142 (max_row, min_row)
143 };
144 let (new_start_column, new_end_column) = if start_column <= end_column {
145 (min_column, max_column)
146 } else {
147 (max_column, min_column)
148 };
149 Ok([new_start_row, new_start_column, new_end_row, new_end_column])
150 }
151
152 pub fn get_selected_sheet(&self) -> u32 {
154 if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
155 view.sheet
156 } else {
157 0
158 }
159 }
160
161 pub fn get_selected_cell(&self) -> (u32, i32, i32) {
163 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
164 view.sheet
165 } else {
166 0
167 };
168 if let Ok(worksheet) = self.model.workbook.worksheet(sheet) {
169 if let Some(view) = worksheet.views.get(&self.model.view_id) {
170 return (sheet, view.row, view.column);
171 }
172 }
173 (0, 1, 1)
175 }
176
177 pub fn get_selected_view(&self) -> SelectedView {
179 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
180 view.sheet
181 } else {
182 0
183 };
184 if let Ok(worksheet) = self.model.workbook.worksheet(sheet) {
185 if let Some(view) = worksheet.views.get(&self.model.view_id) {
186 return SelectedView {
187 sheet,
188 row: view.row,
189 column: view.column,
190 range: view.range,
191 top_row: view.top_row,
192 left_column: view.left_column,
193 };
194 }
195 }
196 SelectedView {
198 sheet: 0,
199 row: 1,
200 column: 1,
201 range: [1, 1, 1, 1],
202 top_row: 1,
203 left_column: 1,
204 }
205 }
206
207 pub fn set_selected_sheet(&mut self, sheet: u32) -> Result<(), String> {
209 if self.model.workbook.worksheet(sheet).is_err() {
210 return Err(format!("Invalid worksheet index {sheet}"));
211 }
212 if let Some(view) = self.model.workbook.views.get_mut(&0) {
213 view.sheet = sheet;
214 }
215 Ok(())
216 }
217
218 pub fn set_selected_cell(&mut self, row: i32, column: i32) -> Result<(), String> {
220 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
221 view.sheet
222 } else {
223 0
224 };
225 if !is_valid_column_number(column) {
226 return Err(format!("Invalid column: '{column}'"));
227 }
228 if !is_valid_row(row) {
229 return Err(format!("Invalid row: '{row}'"));
230 }
231 if self.model.workbook.worksheet(sheet).is_err() {
232 return Err(format!("Invalid worksheet index {sheet}"));
233 }
234 let (row, column) = self.merge_anchor(sheet, row, column)?;
237 let range = self.single_cell_range(sheet, row, column)?;
238 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
239 if let Some(view) = worksheet.views.get_mut(&0) {
240 view.row = row;
241 view.column = column;
242 view.range = range;
243 view.focus_row = row;
244 view.focus_column = column;
245 }
246 }
247 Ok(())
248 }
249
250 pub fn set_selected_range(
252 &mut self,
253 start_row: i32,
254 start_column: i32,
255 end_row: i32,
256 end_column: i32,
257 ) -> Result<(), String> {
258 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
259 view.sheet
260 } else {
261 0
262 };
263
264 if !is_valid_column_number(start_column) {
265 return Err(format!("Invalid column: '{start_column}'"));
266 }
267 if !is_valid_row(start_row) {
268 return Err(format!("Invalid row: '{start_row}'"));
269 }
270
271 if !is_valid_column_number(end_column) {
272 return Err(format!("Invalid column: '{end_column}'"));
273 }
274 if !is_valid_row(end_row) {
275 return Err(format!("Invalid row: '{end_row}'"));
276 }
277 if self.model.workbook.worksheet(sheet).is_err() {
278 return Err(format!("Invalid worksheet index {sheet}"));
279 }
280 let (selected_row, selected_column) = {
281 let worksheet = self.model.workbook.worksheet(sheet)?;
282 match worksheet.views.get(&self.model.view_id) {
283 Some(view) => (view.row, view.column),
284 None => return Ok(()),
285 }
286 };
287 let min_row = start_row.min(end_row);
291 let max_row = start_row.max(end_row);
292 let min_column = start_column.min(end_column);
293 let max_column = start_column.max(end_column);
294 if selected_row < min_row || selected_row > max_row {
295 return Err(format!(
296 "The selected cell is not inside the range. Row: '{selected_row}' and row range '({start_row}, {end_row})'"
297 ));
298 }
299 if selected_column < min_column || selected_column > max_column {
300 return Err(format!(
301 "The selected cell is not inside the range. Column '{selected_column}' and column range '({start_column}, {end_column})'"
302 ));
303 }
304 let focus_row = if selected_row == end_row {
307 start_row
308 } else {
309 end_row
310 };
311 let focus_column = if selected_column == end_column {
312 start_column
313 } else {
314 end_column
315 };
316 let full_columns = start_row == 1 && end_row == LAST_ROW;
320 let full_rows = start_column == 1 && end_column == LAST_COLUMN;
321 let range = if full_columns || full_rows {
322 [min_row, min_column, max_row, max_column]
323 } else {
324 self.grow_range_over_merged_cells(sheet, [min_row, min_column, max_row, max_column])?
325 };
326 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
327 if let Some(view) = worksheet.views.get_mut(&0) {
328 view.range = range;
329 view.focus_row = focus_row;
330 view.focus_column = focus_column;
331 }
332 }
333 Ok(())
334 }
335
336 pub fn on_expand_selected_range(&mut self, key: &str) -> Result<(), String> {
342 let (sheet, window_width, window_height) =
343 if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
344 (
345 view.sheet,
346 view.window_width as f64,
347 view.window_height as f64,
348 )
349 } else {
350 return Ok(());
351 };
352 let (selected_row, selected_column, focus_row, focus_column, range, top_row, left_column) =
353 if let Ok(worksheet) = self.model.workbook.worksheet(sheet) {
354 if let Some(view) = worksheet.views.get(&self.model.view_id) {
355 (
356 view.row,
357 view.column,
358 view.focus_row,
359 view.focus_column,
360 view.range,
361 view.top_row,
362 view.left_column,
363 )
364 } else {
365 return Ok(());
366 }
367 } else {
368 return Ok(());
369 };
370 let [row_start, column_start, row_end, column_end] = range;
371 if ["ArrowUp", "ArrowDown"].contains(&key) && row_start == 1 && row_end == LAST_ROW {
372 return Ok(());
374 }
375 if ["ArrowRight", "ArrowLeft"].contains(&key)
376 && column_start == 1
377 && column_end == LAST_COLUMN
378 {
379 return Ok(());
381 }
382 let frozen_rows = self.model.get_frozen_rows_count(sheet)?;
383 let frozen_columns = self.model.get_frozen_columns_count(sheet)?;
384 let worksheet = self.model.workbook.worksheet(sheet)?;
385 let focus_merge = worksheet
388 .merged_cell_containing(focus_row, focus_column)
389 .cloned();
390
391 let mut new_focus_row = focus_row;
392 let mut new_focus_column = focus_column;
393 match key {
394 "ArrowRight" => {
395 let edge = focus_merge.map_or(focus_column, |m| m.last_column());
396 let mut new_column = edge + 1;
397 while new_column < LAST_COLUMN && worksheet.is_column_hidden(new_column)? {
398 new_column += 1;
399 }
400 if !is_valid_column_number(new_column) {
401 return Ok(());
402 }
403 if new_column > selected_column {
404 let mut width = 0.0;
407 let mut c = left_column;
408 while c <= new_column {
409 width += self.ui_column_width(sheet, c)?;
410 c += 1;
411 }
412 if width > window_width {
413 self.set_top_left_visible_cell(top_row, left_column + 1)?;
414 }
415 }
416 new_focus_column = new_column;
417 }
418 "ArrowLeft" => {
419 let edge = focus_merge.map_or(focus_column, |m| m.column);
420 let mut new_column = edge - 1;
421 while new_column > 1 && worksheet.is_column_hidden(new_column)? {
422 new_column -= 1;
423 }
424 if !is_valid_column_number(new_column) {
425 return Ok(());
426 }
427 if new_column < left_column && new_column > frozen_columns {
429 self.set_top_left_visible_cell(top_row, new_column)?;
430 }
431 new_focus_column = new_column;
432 }
433 "ArrowUp" => {
434 let edge = focus_merge.map_or(focus_row, |m| m.row);
435 let mut new_row = edge - 1;
436 while new_row > 1 && worksheet.is_row_hidden(new_row)? {
437 new_row -= 1;
438 }
439 if !is_valid_row(new_row) {
440 return Ok(());
441 }
442 if new_row < top_row && new_row > frozen_rows {
444 self.set_top_left_visible_cell(new_row, left_column)?;
445 }
446 new_focus_row = new_row;
447 }
448 "ArrowDown" => {
449 let edge = focus_merge.map_or(focus_row, |m| m.last_row());
450 let mut new_row = edge + 1;
451 while new_row < LAST_ROW && worksheet.is_row_hidden(new_row)? {
452 new_row += 1;
453 }
454 if !is_valid_row(new_row) {
455 return Ok(());
456 }
457 if new_row > selected_row {
458 let mut height = 0.0;
461 let mut r = top_row;
462 while r <= new_row + 1 {
463 height += self.ui_row_height(sheet, r)?;
464 r += 1;
465 }
466 if height >= window_height {
467 self.set_top_left_visible_cell(top_row + 1, left_column)?;
468 }
469 }
470 new_focus_row = new_row;
471 }
472 _ => return Ok(()),
473 }
474
475 let new_range = self.selection_range(
476 sheet,
477 selected_row,
478 selected_column,
479 new_focus_row,
480 new_focus_column,
481 )?;
482 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
483 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
484 view.range = new_range;
485 view.focus_row = new_focus_row;
486 view.focus_column = new_focus_column;
487 }
488 }
489 Ok(())
490 }
491
492 pub fn set_top_left_visible_cell(
494 &mut self,
495 top_row: i32,
496 left_column: i32,
497 ) -> Result<(), String> {
498 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
499 view.sheet
500 } else {
501 0
502 };
503
504 if !is_valid_column_number(left_column) {
505 return Err(format!("Invalid column: '{left_column}'"));
506 }
507 if !is_valid_row(top_row) {
508 return Err(format!("Invalid row: '{top_row}'"));
509 }
510 if self.model.workbook.worksheet(sheet).is_err() {
511 return Err(format!("Invalid worksheet index {sheet}"));
512 }
513 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
514 if let Some(view) = worksheet.views.get_mut(&0) {
515 view.top_row = top_row;
516 view.left_column = left_column;
517 }
518 }
519 Ok(())
520 }
521
522 pub fn set_window_width(&mut self, window_width: f64) {
524 if let Some(view) = self.model.workbook.views.get_mut(&self.model.view_id) {
525 view.window_width = window_width as i64;
526 };
527 }
528
529 pub fn get_window_width(&mut self) -> Result<i64, String> {
531 if let Some(view) = self.model.workbook.views.get_mut(&self.model.view_id) {
532 return Ok(view.window_width);
533 };
534 Err("View not found".to_string())
535 }
536
537 pub fn set_window_height(&mut self, window_height: f64) {
539 if let Some(view) = self.model.workbook.views.get_mut(&self.model.view_id) {
540 view.window_height = window_height as i64;
541 };
542 }
543
544 pub fn get_window_height(&mut self) -> Result<i64, String> {
546 if let Some(view) = self.model.workbook.views.get_mut(&self.model.view_id) {
547 return Ok(view.window_height);
548 };
549 Err("View not found".to_string())
550 }
551
552 pub fn on_arrow_right(&mut self) -> Result<(), String> {
554 let (sheet, window_width) =
555 if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
556 (view.sheet, view.window_width)
557 } else {
558 return Err("View not found".to_string());
559 };
560 let worksheet = match self.model.workbook.worksheet(sheet) {
561 Ok(s) => s,
562 Err(_) => return Err("Worksheet not found".to_string()),
563 };
564 let view = match worksheet.views.get(&self.model.view_id) {
565 Some(s) => s,
566 None => return Err("View not found".to_string()),
567 };
568 let row = view.row;
570 let mut new_column = match worksheet.merged_cell_containing(row, view.column) {
571 Some(m) => m.last_column() + 1,
572 None => view.column + 1,
573 };
574 while new_column <= LAST_COLUMN
575 && self
576 .model
577 .workbook
578 .worksheet(sheet)?
579 .is_column_hidden(new_column)?
580 {
581 new_column += 1;
582 }
583 if !is_valid_column_number(new_column) {
584 return Ok(());
585 }
586 let mut width = 0.0;
588 let mut column = view.left_column;
589 while column <= new_column {
590 width += self.ui_column_width(sheet, column)?;
591 column += 1;
592 }
593 let (new_row, new_column) = self.merge_anchor(sheet, row, new_column)?;
595 let new_range = self.single_cell_range(sheet, new_row, new_column)?;
596 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
597 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
598 view.row = new_row;
599 view.column = new_column;
600 view.range = new_range;
601 view.focus_row = new_row;
602 view.focus_column = new_column;
603 if width > window_width as f64 {
604 view.left_column += 1;
605 }
606 }
607 }
608 Ok(())
609 }
610
611 pub fn on_arrow_left(&mut self) -> Result<(), String> {
613 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
614 view.sheet
615 } else {
616 return Err("View not found".to_string());
617 };
618 let worksheet = match self.model.workbook.worksheet(sheet) {
619 Ok(s) => s,
620 Err(_) => return Err("Worksheet not found".to_string()),
621 };
622 let view = match worksheet.views.get(&self.model.view_id) {
623 Some(s) => s,
624 None => return Err("View not found".to_string()),
625 };
626 let row = view.row;
628 let mut new_column = match worksheet.merged_cell_containing(row, view.column) {
629 Some(m) => m.column - 1,
630 None => view.column - 1,
631 };
632 while new_column >= 1
633 && self
634 .model
635 .workbook
636 .worksheet(sheet)?
637 .is_column_hidden(new_column)?
638 {
639 new_column -= 1;
640 }
641 if !is_valid_column_number(new_column) {
642 return Ok(());
643 }
644 let (new_row, new_column) = self.merge_anchor(sheet, row, new_column)?;
646 let new_range = self.single_cell_range(sheet, new_row, new_column)?;
647 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
649 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
650 view.row = new_row;
651 view.column = new_column;
652 view.range = new_range;
653 view.focus_row = new_row;
654 view.focus_column = new_column;
655 if new_column < view.left_column {
656 view.left_column = new_column;
657 }
658 }
659 }
660 Ok(())
661 }
662
663 pub fn on_arrow_up(&mut self) -> Result<(), String> {
665 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
666 view.sheet
667 } else {
668 return Err("View not found".to_string());
669 };
670 let worksheet = match self.model.workbook.worksheet(sheet) {
671 Ok(s) => s,
672 Err(_) => return Err("Worksheet not found".to_string()),
673 };
674 let view = match worksheet.views.get(&self.model.view_id) {
675 Some(s) => s,
676 None => return Err("View not found".to_string()),
677 };
678 let column = view.column;
680 let mut new_row = match worksheet.merged_cell_containing(view.row, column) {
681 Some(m) => m.row - 1,
682 None => view.row - 1,
683 };
684 while new_row >= 1
685 && self
686 .model
687 .workbook
688 .worksheet(sheet)?
689 .is_row_hidden(new_row)?
690 {
691 new_row -= 1;
692 }
693 if !is_valid_row(new_row) {
694 return Ok(());
695 }
696 let (new_row, new_column) = self.merge_anchor(sheet, new_row, column)?;
698 let new_range = self.single_cell_range(sheet, new_row, new_column)?;
699 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
701 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
702 view.row = new_row;
703 view.column = new_column;
704 view.range = new_range;
705 view.focus_row = new_row;
706 view.focus_column = new_column;
707 if new_row < view.top_row {
708 view.top_row = new_row;
709 }
710 }
711 }
712 Ok(())
713 }
714
715 pub fn on_arrow_down(&mut self) -> Result<(), String> {
717 let (sheet, window_height) =
718 if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
719 (view.sheet, view.window_height)
720 } else {
721 return Err("View not found".to_string());
722 };
723 let worksheet = match self.model.workbook.worksheet(sheet) {
724 Ok(s) => s,
725 Err(_) => return Err("Worksheet not found".to_string()),
726 };
727 let view = match worksheet.views.get(&self.model.view_id) {
728 Some(s) => s,
729 None => return Err("View not found".to_string()),
730 };
731 let column = view.column;
733 let mut new_row = match worksheet.merged_cell_containing(view.row, column) {
734 Some(m) => m.last_row() + 1,
735 None => view.row + 1,
736 };
737 while new_row <= LAST_ROW
738 && self
739 .model
740 .workbook
741 .worksheet(sheet)?
742 .is_row_hidden(new_row)?
743 {
744 new_row += 1;
745 }
746 if !is_valid_row(new_row) {
747 return Ok(());
748 }
749 let mut height = 0.0;
751 let mut row = view.top_row;
752 while row <= new_row + 1 && row <= LAST_ROW {
753 height += self.ui_row_height(sheet, row)?;
754 row += 1;
755 }
756 let (new_row, new_column) = self.merge_anchor(sheet, new_row, column)?;
758 let new_range = self.single_cell_range(sheet, new_row, new_column)?;
759 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
760 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
761 view.row = new_row;
762 view.column = new_column;
763 view.range = new_range;
764 view.focus_row = new_row;
765 view.focus_column = new_column;
766 if height > window_height as f64 {
767 view.top_row += 1;
768 }
769 }
770 }
771 Ok(())
772 }
773
774 pub fn get_scroll_x(&self) -> Result<f64, String> {
777 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
778 view.sheet
779 } else {
780 return Err("View not found".to_string());
781 };
782 let worksheet = match self.model.workbook.worksheet(sheet) {
783 Ok(s) => s,
784 Err(_) => return Err("Worksheet not found".to_string()),
785 };
786 let view = match worksheet.views.get(&self.model.view_id) {
787 Some(s) => s,
788 None => return Err("View not found".to_string()),
789 };
790 let mut scroll_x = 0.0;
791 for column in 1..view.left_column {
792 scroll_x += self.ui_column_width(sheet, column)?;
793 }
794 Ok(scroll_x)
795 }
796
797 pub fn get_scroll_y(&self) -> Result<f64, String> {
800 let sheet = if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
801 view.sheet
802 } else {
803 return Err("View not found".to_string());
804 };
805 let worksheet = match self.model.workbook.worksheet(sheet) {
806 Ok(s) => s,
807 Err(_) => return Err("Worksheet not found".to_string()),
808 };
809 let view = match worksheet.views.get(&self.model.view_id) {
810 Some(s) => s,
811 None => return Err("View not found".to_string()),
812 };
813 let mut scroll_y = 0.0;
814 for row in 1..view.top_row {
815 scroll_y += self.ui_row_height(sheet, row)?;
816 }
817 Ok(scroll_y)
818 }
819
820 pub fn on_page_down(&mut self) -> Result<(), String> {
823 let (sheet, window_height) =
824 if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
825 (view.sheet, view.window_height)
826 } else {
827 return Err("View not found".to_string());
828 };
829 let worksheet = match self.model.workbook.worksheet(sheet) {
830 Ok(s) => s,
831 Err(_) => return Err("Worksheet not found".to_string()),
832 };
833 let view = match worksheet.views.get(&self.model.view_id) {
834 Some(s) => s,
835 None => return Err("View not found".to_string()),
836 };
837 let mut last_row = view.top_row;
838 let mut height = self.ui_row_height(sheet, last_row)?;
839 while height <= window_height as f64 {
840 last_row += 1;
841 height += self.ui_row_height(sheet, last_row)?;
842 }
843 if !is_valid_row(last_row) {
844 return Ok(());
845 }
846 let row_delta = view.row - view.top_row;
847 let (new_row, new_column) = self.merge_anchor(sheet, last_row + row_delta, view.column)?;
849 let new_range = self.single_cell_range(sheet, new_row, new_column)?;
850 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
851 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
852 view.top_row = last_row;
853 view.row = new_row;
854 view.column = new_column;
855 view.range = new_range;
856 view.focus_row = new_row;
857 view.focus_column = new_column;
858 }
859 }
860 Ok(())
861 }
862
863 pub fn on_page_up(&mut self) -> Result<(), String> {
865 let (sheet, window_height) =
866 if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
867 (view.sheet, view.window_height as f64)
868 } else {
869 return Err("View not found".to_string());
870 };
871 let worksheet = match self.model.workbook.worksheet(sheet) {
872 Ok(s) => s,
873 Err(_) => return Err("Worksheet not found".to_string()),
874 };
875 let view = match worksheet.views.get(&self.model.view_id) {
876 Some(s) => s,
877 None => return Err("View not found".to_string()),
878 };
879
880 let mut first_row = view.top_row;
881 let mut height = self.ui_row_height(sheet, first_row)?;
882 while height <= window_height && first_row > 1 {
883 first_row -= 1;
884 height += self.ui_row_height(sheet, first_row)?;
885 }
886
887 let row_delta = view.row - view.top_row;
888 let (new_row, new_column) = self.merge_anchor(sheet, first_row + row_delta, view.column)?;
890 let new_range = self.single_cell_range(sheet, new_row, new_column)?;
891 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
892 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
893 view.top_row = first_row;
894 view.row = new_row;
895 view.column = new_column;
896 view.range = new_range;
897 view.focus_row = new_row;
898 view.focus_column = new_column;
899 }
900 }
901 Ok(())
902 }
903
904 pub fn on_area_selecting(&mut self, target_row: i32, target_column: i32) -> Result<(), String> {
906 let (sheet, window_width, window_height) =
907 if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
908 (
909 view.sheet,
910 view.window_width as f64,
911 view.window_height as f64,
912 )
913 } else {
914 return Ok(());
915 };
916 let (selected_row, selected_column, top_row, left_column) =
917 if let Ok(worksheet) = self.model.workbook.worksheet(sheet) {
918 if let Some(view) = worksheet.views.get(&self.model.view_id) {
919 (view.row, view.column, view.top_row, view.left_column)
920 } else {
921 return Ok(());
922 }
923 } else {
924 return Ok(());
925 };
926 let frozen_rows = self.model.get_frozen_rows_count(sheet)?;
927 let frozen_columns = self.model.get_frozen_columns_count(sheet)?;
928
929 let mut new_left_column = left_column;
930 if target_column <= frozen_columns {
931 } else if target_column >= selected_column {
934 let mut width = 0.0;
935 let mut column = left_column;
936 while column <= target_column {
937 width += self.ui_column_width(sheet, column)?;
938 column += 1;
939 }
940
941 while width > window_width {
942 width -= self.ui_column_width(sheet, new_left_column)?;
943 new_left_column += 1;
944 }
945 } else if target_column < new_left_column {
946 new_left_column = target_column;
947 }
948 let mut new_top_row = top_row;
949 if target_row <= frozen_rows {
950 } else if target_row >= selected_row {
953 let mut height = 0.0;
954 let mut row = top_row;
955 while row <= target_row {
956 height += self.ui_row_height(sheet, row)?;
957 row += 1;
958 }
959 while height > window_height {
960 height -= self.ui_row_height(sheet, new_top_row)?;
961 new_top_row += 1;
962 }
963 } else if target_row < new_top_row {
964 new_top_row = target_row;
965 }
966
967 let range = self.selection_range(
971 sheet,
972 selected_row,
973 selected_column,
974 target_row,
975 target_column,
976 )?;
977 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
978 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
979 view.range = range;
980 view.focus_row = target_row;
981 view.focus_column = target_column;
982 if new_top_row != top_row {
983 view.top_row = new_top_row;
984 }
985 if new_left_column != left_column {
986 view.left_column = new_left_column;
987 }
988 }
989 }
990
991 Ok(())
992 }
993
994 pub fn on_navigate_to_edge_in_direction(
996 &mut self,
997 direction: NavigationDirection,
998 ) -> Result<(), String> {
999 let (sheet, window_height, window_width) =
1000 if let Some(view) = self.model.workbook.views.get(&self.model.view_id) {
1001 (view.sheet, view.window_height, view.window_width)
1002 } else {
1003 return Err("View not found".to_string());
1004 };
1005 let worksheet = match self.model.workbook.worksheet(sheet) {
1006 Ok(s) => s,
1007 Err(_) => return Err("Worksheet not found".to_string()),
1008 };
1009 let view = match worksheet.views.get(&self.model.view_id) {
1010 Some(s) => s,
1011 None => return Err("View not found".to_string()),
1012 };
1013 let row = view.row;
1014 let column = view.column;
1015 if !is_valid_row(row) || !is_valid_column_number(column) {
1016 return Err("Invalid row or column".to_string());
1017 }
1018 let (new_row, new_column) =
1019 worksheet.navigate_to_edge_in_direction(row, column, direction)?;
1020 if !is_valid_row(new_row) || !is_valid_column_number(new_column) {
1021 return Err("Invalid row or column after navigation".to_string());
1022 }
1023 if new_row == row && new_column == column {
1024 return Ok(()); }
1026
1027 let mut top_row = view.top_row;
1028 let mut left_column = view.left_column;
1029
1030 match direction {
1031 NavigationDirection::Left | NavigationDirection::Right => {
1032 if new_column < view.left_column {
1037 left_column = new_column;
1038 } else {
1039 let mut c = new_column;
1040 let mut width = self.ui_column_width(sheet, c)?;
1041 while c > 1 && width <= window_width as f64 {
1042 c -= 1;
1043 width += self.ui_column_width(sheet, c)?;
1044 }
1045 if c > view.left_column {
1046 left_column = c;
1047 }
1048 }
1049 }
1050 NavigationDirection::Up | NavigationDirection::Down => {
1051 if new_row < view.top_row {
1056 top_row = new_row;
1057 } else {
1058 let mut r = new_row;
1059 let mut height = self.ui_row_height(sheet, r)?;
1060 while r > 1 && height <= window_height as f64 {
1061 r -= 1;
1062 height += self.ui_row_height(sheet, r)?;
1063 }
1064 if r > view.top_row {
1065 top_row = r;
1066 }
1067 }
1068 }
1069 }
1070
1071 let (new_row, new_column) = self.merge_anchor(sheet, new_row, new_column)?;
1073 let new_range = self.single_cell_range(sheet, new_row, new_column)?;
1074 if let Ok(worksheet) = self.model.workbook.worksheet_mut(sheet) {
1075 if let Some(view) = worksheet.views.get_mut(&self.model.view_id) {
1076 view.row = new_row;
1077 view.column = new_column;
1078 view.range = new_range;
1079 view.focus_row = new_row;
1080 view.focus_column = new_column;
1081
1082 view.top_row = top_row;
1083 view.left_column = left_column;
1084 }
1085 }
1086 Ok(())
1087 }
1088}