Skip to main content

ironcalc_base/user_model/
links.rs

1use std::collections::HashMap;
2
3use crate::links::{CellLinkView, THEME_COLOR_HYPERLINK};
4use crate::types::{Cell, Color, Link};
5
6use super::{common::UserModel, history::Diff};
7
8impl UserModel<'_> {
9    /// Returns the link attached to cell (`row`, `column`) or `None` if there isn't one.
10    pub fn get_cell_link(&self, sheet: u32, row: i32, column: i32) -> Result<Option<Link>, String> {
11        self.model.get_cell_link(sheet, row, column)
12    }
13
14    /// Returns all the links in the worksheet, keyed by (row, column).
15    pub fn get_links(&self, sheet: u32) -> Result<&HashMap<(i32, i32), Link>, String> {
16        self.model.get_links(sheet)
17    }
18
19    /// Returns all the links in the worksheet as a list sorted by (row, column).
20    pub fn get_links_list(&self, sheet: u32) -> Result<Vec<CellLinkView>, String> {
21        self.model.get_links_list(sheet)
22    }
23
24    /// Attaches `link` to cell (`row`, `column`), replacing the existing link if there
25    /// was one.
26    ///
27    /// If `label` is given it becomes the content of the cell (the displayed text of
28    /// the link). When the cell did not have a link before, the link style (underline
29    /// and the theme hyperlink color) is applied to the cell. The style is ordinary
30    /// cell formatting: it can be changed afterwards and deleting the link does not
31    /// remove it.
32    ///
33    /// The whole operation is a single entry in the undo/redo history.
34    pub fn set_cell_link(
35        &mut self,
36        sheet: u32,
37        row: i32,
38        column: i32,
39        link: Link,
40        label: Option<&str>,
41    ) -> Result<(), String> {
42        let old_link = self.model.get_cell_link(sheet, row, column)?;
43        let is_new_link = old_link.is_none();
44        let mut diff_list = Vec::new();
45        let mut needs_evaluation = false;
46
47        if old_link.as_ref() != Some(&link) {
48            self.model.set_cell_link(sheet, row, column, link.clone())?;
49            diff_list.push(Diff::SetCellLink {
50                sheet,
51                row,
52                column,
53                old_value: Box::new(old_link),
54                new_value: Box::new(Some(link)),
55            });
56        }
57
58        if let Some(label) = label {
59            if label != self.model.get_formatted_cell_value(sheet, row, column)? {
60                let old_value = self
61                    .model
62                    .workbook
63                    .worksheet(sheet)?
64                    .cell(row, column)
65                    .cloned();
66                // If it is a spill cell we want to save the old value as None, because
67                // the value of a spill cell is determined by the anchor cell
68                let old_value = if matches!(old_value, Some(Cell::SpillCell { .. })) {
69                    None
70                } else {
71                    old_value
72                };
73                self.model
74                    .set_user_input(sheet, row, column, label.to_string())?;
75                needs_evaluation = true;
76                diff_list.push(Diff::SetCellValue {
77                    sheet,
78                    row,
79                    column,
80                    new_value: label.to_string(),
81                    old_value: Box::new(old_value),
82                });
83            }
84        }
85
86        if is_new_link {
87            let old_style = self.model.get_cell_style_or_none(sheet, row, column)?;
88            let mut style = self.model.get_style_for_cell(sheet, row, column)?;
89            style.font.u = true;
90            style.font.color = Color::Theme(THEME_COLOR_HYPERLINK, 0.0);
91            self.model.set_cell_style(sheet, row, column, &style)?;
92            diff_list.push(Diff::SetCellStyle {
93                sheet,
94                row,
95                column,
96                old_value: Box::new(old_style),
97                new_value: Box::new(style),
98            });
99        }
100
101        if diff_list.is_empty() {
102            // no-op, don't pollute the undo history
103            return Ok(());
104        }
105        self.push_diff_list(diff_list);
106        if needs_evaluation {
107            self.evaluate_if_not_paused();
108        }
109        Ok(())
110    }
111
112    /// Removes the link attached to cell (`row`, `column`). It is not an error if the
113    /// cell has no link. The cell content and the cell style are left untouched.
114    pub fn delete_cell_link(&mut self, sheet: u32, row: i32, column: i32) -> Result<(), String> {
115        let old_value = self.model.get_cell_link(sheet, row, column)?;
116        if old_value.is_none() {
117            return Ok(());
118        }
119        self.model.delete_cell_link(sheet, row, column)?;
120        self.push_diff_list(vec![Diff::SetCellLink {
121            sheet,
122            row,
123            column,
124            old_value: Box::new(old_value),
125            new_value: Box::new(None),
126        }]);
127        Ok(())
128    }
129}