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                .columns_in_row(row);
196            for column in columns {
197                self.update_single_cell_border(
198                    border_area,
199                    (sheet, row, column),
200                    (first_row, 1, last_row, LAST_COLUMN),
201                    &mut diff_list,
202                )?;
203            }
204
205            self.model.set_row_style(sheet, row, &new_value)?;
206            diff_list.push(Diff::SetRowStyle {
207                sheet,
208                row,
209                old_value: Box::new(old_value),
210                new_value: Box::new(new_value),
211            });
212        }
213        // TODO: We need to check the rows above and below. also any non empty cell in the rows above and below.
214        self.push_diff_list(diff_list);
215        Ok(())
216    }
217
218    fn set_columns_with_border(
219        &mut self,
220        sheet: u32,
221        first_column: i32,
222        last_column: i32,
223        border_area: &BorderArea,
224    ) -> Result<(), String> {
225        let mut diff_list = Vec::new();
226        // We need all the rows in the column to update the style
227        // NB: This is too much, this is all the rows that have values
228        let data_rows: Vec<i32> = self.model.workbook.worksheet(sheet)?.sheet_data.rows();
229        let styled_rows = &self.model.workbook.worksheet(sheet)?.rows.clone();
230        for column in first_column..=last_column {
231            let old_value = self.model.get_column_style(sheet, column)?;
232            let mut new_value = match &old_value {
233                Some(value) => value.clone(),
234                None => Default::default(),
235            };
236
237            match border_area.r#type {
238                BorderType::All => {
239                    new_value.border.top = Some(border_area.item.clone());
240                    new_value.border.right = Some(border_area.item.clone());
241                    new_value.border.bottom = Some(border_area.item.clone());
242                    new_value.border.left = Some(border_area.item.clone());
243                }
244                BorderType::Inner => {
245                    if column != first_column {
246                        new_value.border.left = Some(border_area.item.clone());
247                    }
248                    if column != last_column {
249                        new_value.border.right = Some(border_area.item.clone());
250                    }
251                }
252                BorderType::Outer => {
253                    if column == first_column {
254                        new_value.border.left = Some(border_area.item.clone());
255                    }
256                    if column == last_column {
257                        new_value.border.right = Some(border_area.item.clone());
258                    }
259                }
260                BorderType::Top => {
261                    // noop
262                }
263                BorderType::Right => {
264                    if column == last_column {
265                        new_value.border.right = Some(border_area.item.clone());
266                    }
267                }
268                BorderType::Bottom => {
269                    // noop
270                }
271                BorderType::Left => {
272                    if column == first_column {
273                        new_value.border.left = Some(border_area.item.clone());
274                    }
275                }
276                BorderType::CenterH => {
277                    new_value.border.top = Some(border_area.item.clone());
278                    new_value.border.bottom = Some(border_area.item.clone());
279                }
280                BorderType::CenterV => {
281                    if column != first_column {
282                        new_value.border.left = Some(border_area.item.clone());
283                    }
284                    if column != last_column {
285                        new_value.border.right = Some(border_area.item.clone());
286                    }
287                }
288                BorderType::None => {
289                    new_value.border.top = None;
290                    new_value.border.right = None;
291                    new_value.border.bottom = None;
292                    new_value.border.left = None;
293                }
294            }
295            // We need to go through each non empty cell in the column
296            for &row in &data_rows {
297                if self
298                    .model
299                    .workbook
300                    .worksheet(sheet)?
301                    .cell(row, column)
302                    .is_some()
303                {
304                    self.update_single_cell_border(
305                        border_area,
306                        (sheet, row, column),
307                        (1, first_column, LAST_ROW, last_column),
308                        &mut diff_list,
309                    )?;
310                }
311            }
312
313            // We also need to overwrite those that have a row style
314            for row_s in styled_rows.iter() {
315                let row = row_s.r;
316                self.update_single_cell_border(
317                    border_area,
318                    (sheet, row, column),
319                    (1, first_column, LAST_ROW, last_column),
320                    &mut diff_list,
321                )?;
322            }
323
324            self.model.set_column_style(sheet, column, &new_value)?;
325            diff_list.push(Diff::SetColumnStyle {
326                sheet,
327                column,
328                old_value: Box::new(old_value),
329                new_value: Box::new(new_value),
330            });
331        }
332        // We need to check the borders of the column to the left and the column to the right
333        // We also need to check every non-empty cell in the columns to the left and right
334        self.push_diff_list(diff_list);
335        Ok(())
336    }
337
338    /// Sets the border in an area of cells.
339    /// When setting the border we need to check if the adjacent cells have a "heavier" border
340    /// If that is the case we need to change it
341    pub fn set_area_with_border(
342        &mut self,
343        range: &Area,
344        border_area: &BorderArea,
345    ) -> Result<(), String> {
346        let sheet = range.sheet;
347        let first_row = range.row;
348        let first_column = range.column;
349        let last_row = first_row + range.height - 1;
350        let last_column = first_column + range.width - 1;
351        if first_row == 1 && last_row == LAST_ROW {
352            // full columns
353            self.set_columns_with_border(sheet, first_column, last_column, border_area)?;
354            return Ok(());
355        }
356        if first_column == 1 && last_column == LAST_COLUMN {
357            // full rows
358            self.set_rows_with_border(sheet, first_row, last_row, border_area)?;
359            return Ok(());
360        }
361        let mut diff_list = Vec::new();
362        for row in first_row..=last_row {
363            for column in first_column..=last_column {
364                self.update_single_cell_border(
365                    border_area,
366                    (sheet, row, column),
367                    (first_row, first_column, last_row, last_column),
368                    &mut diff_list,
369                )?;
370            }
371        }
372
373        // bottom of the cells above the first
374        if first_row > 1
375            && [
376                BorderType::All,
377                BorderType::None,
378                BorderType::Outer,
379                BorderType::Top,
380            ]
381            .contains(&border_area.r#type)
382        {
383            let row = first_row - 1;
384            for column in first_column..=last_column {
385                let old_value = self.model.get_style_for_cell(sheet, row, column)?;
386                if is_max_border(Some(&border_area.item), old_value.border.bottom.as_ref()) {
387                    let mut style = old_value.clone();
388                    if border_area.r#type == BorderType::None {
389                        style.border.bottom = None;
390                    } else {
391                        style.border.bottom = Some(border_area.item.clone());
392                    }
393                    self.model.set_cell_style(sheet, row, column, &style)?;
394                    diff_list.push(Diff::SetCellStyle {
395                        sheet,
396                        row,
397                        column,
398                        old_value: Box::new(Some(old_value)),
399                        new_value: Box::new(style),
400                    });
401                }
402            }
403        }
404        // Cells to the right
405        if last_column < LAST_COLUMN
406            && [
407                BorderType::All,
408                BorderType::None,
409                BorderType::Outer,
410                BorderType::Right,
411            ]
412            .contains(&border_area.r#type)
413        {
414            let column = last_column + 1;
415            for row in first_row..=last_row {
416                let old_value = self.model.get_style_for_cell(sheet, row, column)?;
417                // If the border in the adjacent cell is "heavier" we change it
418                if is_max_border(Some(&border_area.item), old_value.border.left.as_ref()) {
419                    let mut style = old_value.clone();
420                    if border_area.r#type == BorderType::None {
421                        style.border.left = None;
422                    } else {
423                        style.border.left = Some(border_area.item.clone());
424                    }
425                    self.model.set_cell_style(sheet, row, column, &style)?;
426                    diff_list.push(Diff::SetCellStyle {
427                        sheet,
428                        row,
429                        column,
430                        old_value: Box::new(Some(old_value)),
431                        new_value: Box::new(style),
432                    });
433                }
434            }
435        }
436        // Cells bellow
437        if last_row < LAST_ROW
438            && [
439                BorderType::All,
440                BorderType::None,
441                BorderType::Outer,
442                BorderType::Bottom,
443            ]
444            .contains(&border_area.r#type)
445        {
446            let row = last_row + 1;
447            for column in first_column..=last_column {
448                let old_value = self.model.get_style_for_cell(sheet, row, column)?;
449                if is_max_border(Some(&border_area.item), old_value.border.top.as_ref()) {
450                    let mut style = old_value.clone();
451                    if border_area.r#type == BorderType::None {
452                        style.border.top = None;
453                    } else {
454                        style.border.top = Some(border_area.item.clone());
455                    }
456                    self.model.set_cell_style(sheet, row, column, &style)?;
457                    diff_list.push(Diff::SetCellStyle {
458                        sheet,
459                        row,
460                        column,
461                        old_value: Box::new(Some(old_value)),
462                        new_value: Box::new(style),
463                    });
464                }
465            }
466        }
467        // Cells to the left
468        if first_column > 1
469            && [
470                BorderType::All,
471                BorderType::None,
472                BorderType::Outer,
473                BorderType::Left,
474            ]
475            .contains(&border_area.r#type)
476        {
477            let column = first_column - 1;
478            for row in first_row..=last_row {
479                let old_value = self.model.get_style_for_cell(sheet, row, column)?;
480                if is_max_border(Some(&border_area.item), old_value.border.right.as_ref()) {
481                    let mut style = old_value.clone();
482                    if border_area.r#type == BorderType::None {
483                        style.border.right = None;
484                    } else {
485                        style.border.right = Some(border_area.item.clone());
486                    }
487                    self.model.set_cell_style(sheet, row, column, &style)?;
488                    diff_list.push(Diff::SetCellStyle {
489                        sheet,
490                        row,
491                        column,
492                        old_value: Box::new(Some(old_value)),
493                        new_value: Box::new(style),
494                    });
495                }
496            }
497        }
498
499        self.push_diff_list(diff_list);
500        Ok(())
501    }
502}