Skip to main content

ironcalc_base/user_model/
border.rs

1use crate::{
2    constants::{LAST_COLUMN, LAST_ROW},
3    expressions::types::Area,
4};
5
6use super::{
7    border_utils::is_max_border, common::BorderType, history::Diff, BorderArea, UserModel,
8};
9
10impl<'a> UserModel<'a> {
11    fn update_single_cell_border(
12        &mut self,
13        border_area: &BorderArea,
14        cell: (u32, i32, i32),
15        range: (i32, i32, i32, i32),
16        diff_list: &mut Vec<Diff>,
17    ) -> Result<(), String> {
18        let (sheet, row, column) = cell;
19        let (first_row, first_column, last_row, last_column) = range;
20
21        let old_value = self.model.get_cell_style_or_none(sheet, row, column)?;
22        let mut new_value = match &old_value {
23            Some(value) => value.clone(),
24            None => Default::default(),
25        };
26        match border_area.r#type {
27            BorderType::All => {
28                new_value.border.top = Some(border_area.item.clone());
29                new_value.border.right = Some(border_area.item.clone());
30                new_value.border.bottom = Some(border_area.item.clone());
31                new_value.border.left = Some(border_area.item.clone());
32            }
33            BorderType::Inner => {
34                if row != first_row {
35                    new_value.border.top = Some(border_area.item.clone());
36                }
37                if row != last_row {
38                    new_value.border.bottom = Some(border_area.item.clone());
39                }
40                if column != first_column {
41                    new_value.border.left = Some(border_area.item.clone());
42                }
43                if column != last_column {
44                    new_value.border.right = Some(border_area.item.clone());
45                }
46            }
47            BorderType::Outer => {
48                if row == first_row {
49                    new_value.border.top = Some(border_area.item.clone());
50                }
51                if row == last_row {
52                    new_value.border.bottom = Some(border_area.item.clone());
53                }
54                if column == first_column {
55                    new_value.border.left = Some(border_area.item.clone());
56                }
57                if column == last_column {
58                    new_value.border.right = Some(border_area.item.clone());
59                }
60            }
61            BorderType::Top => {
62                if row == first_row {
63                    new_value.border.top = Some(border_area.item.clone());
64                }
65            }
66            BorderType::Right => {
67                if column == last_column {
68                    new_value.border.right = Some(border_area.item.clone());
69                }
70            }
71            BorderType::Bottom => {
72                if row == last_row {
73                    new_value.border.bottom = Some(border_area.item.clone());
74                }
75            }
76            BorderType::Left => {
77                if column == first_column {
78                    new_value.border.left = Some(border_area.item.clone());
79                }
80            }
81            BorderType::CenterH => {
82                if row != first_row {
83                    new_value.border.top = Some(border_area.item.clone());
84                }
85                if row != last_row {
86                    new_value.border.bottom = Some(border_area.item.clone());
87                }
88            }
89            BorderType::CenterV => {
90                if column != first_column {
91                    new_value.border.left = Some(border_area.item.clone());
92                }
93                if column != last_column {
94                    new_value.border.right = Some(border_area.item.clone());
95                }
96            }
97            BorderType::None => {
98                new_value.border.top = None;
99                new_value.border.right = None;
100                new_value.border.bottom = None;
101                new_value.border.left = None;
102            }
103        }
104        self.model.set_cell_style(sheet, row, column, &new_value)?;
105        diff_list.push(Diff::SetCellStyle {
106            sheet,
107            row,
108            column,
109            old_value: Box::new(old_value),
110            new_value: Box::new(new_value),
111        });
112        Ok(())
113    }
114
115    fn set_rows_with_border(
116        &mut self,
117        sheet: u32,
118        first_row: i32,
119        last_row: i32,
120        border_area: &BorderArea,
121    ) -> Result<(), String> {
122        let mut diff_list = Vec::new();
123        for row in first_row..=last_row {
124            let old_value = self.model.get_row_style(sheet, row)?;
125            let mut new_value = match &old_value {
126                Some(value) => value.clone(),
127                None => Default::default(),
128            };
129
130            match border_area.r#type {
131                BorderType::All => {
132                    new_value.border.top = Some(border_area.item.clone());
133                    new_value.border.right = Some(border_area.item.clone());
134                    new_value.border.bottom = Some(border_area.item.clone());
135                    new_value.border.left = Some(border_area.item.clone());
136                }
137                BorderType::Inner => {
138                    if row != first_row {
139                        new_value.border.top = Some(border_area.item.clone());
140                    }
141                    if row != last_row {
142                        new_value.border.bottom = Some(border_area.item.clone());
143                    }
144                }
145                BorderType::Outer => {
146                    if row == first_row {
147                        new_value.border.top = Some(border_area.item.clone());
148                    }
149                    if row == last_row {
150                        new_value.border.bottom = Some(border_area.item.clone());
151                    }
152                }
153                BorderType::Top => {
154                    if row == first_row {
155                        new_value.border.top = Some(border_area.item.clone());
156                    }
157                }
158                BorderType::Right => {
159                    // noop
160                }
161                BorderType::Bottom => {
162                    if row == last_row {
163                        new_value.border.bottom = Some(border_area.item.clone());
164                    }
165                }
166                BorderType::Left => {
167                    // noop
168                }
169                BorderType::CenterH => {
170                    if row != first_row {
171                        new_value.border.top = Some(border_area.item.clone());
172                    }
173                    if row != last_row {
174                        new_value.border.bottom = Some(border_area.item.clone());
175                    }
176                }
177                BorderType::CenterV => {
178                    new_value.border.left = Some(border_area.item.clone());
179                    new_value.border.right = Some(border_area.item.clone());
180                }
181                BorderType::None => {
182                    new_value.border.top = None;
183                    new_value.border.right = None;
184                    new_value.border.bottom = None;
185                    new_value.border.left = None;
186                }
187            }
188
189            // We need to go throw each non-empty cell in the row
190            let columns: Vec<i32> = self
191                .model
192                .workbook
193                .worksheet(sheet)?
194                .sheet_data
195                .get(&row)
196                .map(|row_data| row_data.keys().copied().collect())
197                .unwrap_or_default();
198            for column in columns {
199                self.update_single_cell_border(
200                    border_area,
201                    (sheet, row, column),
202                    (first_row, 1, last_row, LAST_COLUMN),
203                    &mut diff_list,
204                )?;
205            }
206
207            self.model.set_row_style(sheet, row, &new_value)?;
208            diff_list.push(Diff::SetRowStyle {
209                sheet,
210                row,
211                old_value: Box::new(old_value),
212                new_value: Box::new(new_value),
213            });
214        }
215        // TODO: We need to check the rows above and below. also any non empty cell in the rows above and below.
216        self.push_diff_list(diff_list);
217        Ok(())
218    }
219
220    fn set_columns_with_border(
221        &mut self,
222        sheet: u32,
223        first_column: i32,
224        last_column: i32,
225        border_area: &BorderArea,
226    ) -> Result<(), String> {
227        let mut diff_list = Vec::new();
228        // We need all the rows in the column to update the style
229        // NB: This is too much, this is all the rows that have values
230        let data_rows: Vec<i32> = self
231            .model
232            .workbook
233            .worksheet(sheet)?
234            .sheet_data
235            .keys()
236            .copied()
237            .collect();
238        let styled_rows = &self.model.workbook.worksheet(sheet)?.rows.clone();
239        for column in first_column..=last_column {
240            let old_value = self.model.get_column_style(sheet, column)?;
241            let mut new_value = match &old_value {
242                Some(value) => value.clone(),
243                None => Default::default(),
244            };
245
246            match border_area.r#type {
247                BorderType::All => {
248                    new_value.border.top = Some(border_area.item.clone());
249                    new_value.border.right = Some(border_area.item.clone());
250                    new_value.border.bottom = Some(border_area.item.clone());
251                    new_value.border.left = Some(border_area.item.clone());
252                }
253                BorderType::Inner => {
254                    if column != first_column {
255                        new_value.border.left = Some(border_area.item.clone());
256                    }
257                    if column != last_column {
258                        new_value.border.right = Some(border_area.item.clone());
259                    }
260                }
261                BorderType::Outer => {
262                    if column == first_column {
263                        new_value.border.left = Some(border_area.item.clone());
264                    }
265                    if column == last_column {
266                        new_value.border.right = Some(border_area.item.clone());
267                    }
268                }
269                BorderType::Top => {
270                    // noop
271                }
272                BorderType::Right => {
273                    if column == last_column {
274                        new_value.border.right = Some(border_area.item.clone());
275                    }
276                }
277                BorderType::Bottom => {
278                    // noop
279                }
280                BorderType::Left => {
281                    if column == first_column {
282                        new_value.border.left = Some(border_area.item.clone());
283                    }
284                }
285                BorderType::CenterH => {
286                    new_value.border.top = Some(border_area.item.clone());
287                    new_value.border.bottom = Some(border_area.item.clone());
288                }
289                BorderType::CenterV => {
290                    if column != first_column {
291                        new_value.border.left = Some(border_area.item.clone());
292                    }
293                    if column != last_column {
294                        new_value.border.right = Some(border_area.item.clone());
295                    }
296                }
297                BorderType::None => {
298                    new_value.border.top = None;
299                    new_value.border.right = None;
300                    new_value.border.bottom = None;
301                    new_value.border.left = None;
302                }
303            }
304            // We need to go through each non empty cell in the column
305            for &row in &data_rows {
306                if let Some(data_row) = self.model.workbook.worksheet(sheet)?.sheet_data.get(&row) {
307                    if data_row.get(&column).is_some() {
308                        self.update_single_cell_border(
309                            border_area,
310                            (sheet, row, column),
311                            (1, first_column, LAST_ROW, last_column),
312                            &mut diff_list,
313                        )?;
314                    }
315                }
316            }
317
318            // We also need to overwrite those that have a row style
319            for row_s in styled_rows.iter() {
320                let row = row_s.r;
321                self.update_single_cell_border(
322                    border_area,
323                    (sheet, row, column),
324                    (1, first_column, LAST_ROW, last_column),
325                    &mut diff_list,
326                )?;
327            }
328
329            self.model.set_column_style(sheet, column, &new_value)?;
330            diff_list.push(Diff::SetColumnStyle {
331                sheet,
332                column,
333                old_value: Box::new(old_value),
334                new_value: Box::new(new_value),
335            });
336        }
337        // We need to check the borders of the column to the left and the column to the right
338        // We also need to check every non-empty cell in the columns to the left and right
339        self.push_diff_list(diff_list);
340        Ok(())
341    }
342
343    /// Sets the border in an area of cells.
344    /// When setting the border we need to check if the adjacent cells have a "heavier" border
345    /// If that is the case we need to change it
346    pub fn set_area_with_border(
347        &mut self,
348        range: &Area,
349        border_area: &BorderArea,
350    ) -> Result<(), String> {
351        let sheet = range.sheet;
352        let first_row = range.row;
353        let first_column = range.column;
354        let last_row = first_row + range.height - 1;
355        let last_column = first_column + range.width - 1;
356        if first_row == 1 && last_row == LAST_ROW {
357            // full columns
358            self.set_columns_with_border(sheet, first_column, last_column, border_area)?;
359            return Ok(());
360        }
361        if first_column == 1 && last_column == LAST_COLUMN {
362            // full rows
363            self.set_rows_with_border(sheet, first_row, last_row, border_area)?;
364            return Ok(());
365        }
366        let mut diff_list = Vec::new();
367        for row in first_row..=last_row {
368            for column in first_column..=last_column {
369                self.update_single_cell_border(
370                    border_area,
371                    (sheet, row, column),
372                    (first_row, first_column, last_row, last_column),
373                    &mut diff_list,
374                )?;
375            }
376        }
377
378        // bottom of the cells above the first
379        if first_row > 1
380            && [
381                BorderType::All,
382                BorderType::None,
383                BorderType::Outer,
384                BorderType::Top,
385            ]
386            .contains(&border_area.r#type)
387        {
388            let row = first_row - 1;
389            for column in first_column..=last_column {
390                let old_value = self.model.get_style_for_cell(sheet, row, column)?;
391                if is_max_border(Some(&border_area.item), old_value.border.bottom.as_ref()) {
392                    let mut style = old_value.clone();
393                    if border_area.r#type == BorderType::None {
394                        style.border.bottom = None;
395                    } else {
396                        style.border.bottom = Some(border_area.item.clone());
397                    }
398                    self.model.set_cell_style(sheet, row, column, &style)?;
399                    diff_list.push(Diff::SetCellStyle {
400                        sheet,
401                        row,
402                        column,
403                        old_value: Box::new(Some(old_value)),
404                        new_value: Box::new(style),
405                    });
406                }
407            }
408        }
409        // Cells to the right
410        if last_column < LAST_COLUMN
411            && [
412                BorderType::All,
413                BorderType::None,
414                BorderType::Outer,
415                BorderType::Right,
416            ]
417            .contains(&border_area.r#type)
418        {
419            let column = last_column + 1;
420            for row in first_row..=last_row {
421                let old_value = self.model.get_style_for_cell(sheet, row, column)?;
422                // If the border in the adjacent cell is "heavier" we change it
423                if is_max_border(Some(&border_area.item), old_value.border.left.as_ref()) {
424                    let mut style = old_value.clone();
425                    if border_area.r#type == BorderType::None {
426                        style.border.left = None;
427                    } else {
428                        style.border.left = Some(border_area.item.clone());
429                    }
430                    self.model.set_cell_style(sheet, row, column, &style)?;
431                    diff_list.push(Diff::SetCellStyle {
432                        sheet,
433                        row,
434                        column,
435                        old_value: Box::new(Some(old_value)),
436                        new_value: Box::new(style),
437                    });
438                }
439            }
440        }
441        // Cells bellow
442        if last_row < LAST_ROW
443            && [
444                BorderType::All,
445                BorderType::None,
446                BorderType::Outer,
447                BorderType::Bottom,
448            ]
449            .contains(&border_area.r#type)
450        {
451            let row = last_row + 1;
452            for column in first_column..=last_column {
453                let old_value = self.model.get_style_for_cell(sheet, row, column)?;
454                if is_max_border(Some(&border_area.item), old_value.border.top.as_ref()) {
455                    let mut style = old_value.clone();
456                    if border_area.r#type == BorderType::None {
457                        style.border.top = None;
458                    } else {
459                        style.border.top = Some(border_area.item.clone());
460                    }
461                    self.model.set_cell_style(sheet, row, column, &style)?;
462                    diff_list.push(Diff::SetCellStyle {
463                        sheet,
464                        row,
465                        column,
466                        old_value: Box::new(Some(old_value)),
467                        new_value: Box::new(style),
468                    });
469                }
470            }
471        }
472        // Cells to the left
473        if first_column > 1
474            && [
475                BorderType::All,
476                BorderType::None,
477                BorderType::Outer,
478                BorderType::Left,
479            ]
480            .contains(&border_area.r#type)
481        {
482            let column = first_column - 1;
483            for row in first_row..=last_row {
484                let old_value = self.model.get_style_for_cell(sheet, row, column)?;
485                if is_max_border(Some(&border_area.item), old_value.border.right.as_ref()) {
486                    let mut style = old_value.clone();
487                    if border_area.r#type == BorderType::None {
488                        style.border.right = None;
489                    } else {
490                        style.border.right = Some(border_area.item.clone());
491                    }
492                    self.model.set_cell_style(sheet, row, column, &style)?;
493                    diff_list.push(Diff::SetCellStyle {
494                        sheet,
495                        row,
496                        column,
497                        old_value: Box::new(Some(old_value)),
498                        new_value: Box::new(style),
499                    });
500                }
501            }
502        }
503
504        self.push_diff_list(diff_list);
505        Ok(())
506    }
507}