UPDATE: Autofill by rows (#73)
This commit is contained in:
committed by
GitHub
parent
c3a9b006d2
commit
72c7c94f3d
@@ -1,4 +1,5 @@
|
||||
mod test_add_delete_sheets;
|
||||
mod test_autofill;
|
||||
mod test_clear_cells;
|
||||
mod test_diff_queue;
|
||||
mod test_evaluation;
|
||||
|
||||
399
base/src/test/user_model/test_autofill.rs
Normal file
399
base/src/test/user_model/test_autofill.rs
Normal file
@@ -0,0 +1,399 @@
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use crate::constants::{LAST_COLUMN, LAST_ROW};
|
||||
use crate::expressions::types::Area;
|
||||
use crate::test::util::new_empty_model;
|
||||
use crate::UserModel;
|
||||
|
||||
#[test]
|
||||
fn basic_tests() {
|
||||
let model = new_empty_model();
|
||||
let mut model = UserModel::from_model(model);
|
||||
// This is cell A3
|
||||
model.set_user_input(0, 3, 1, "alpha").unwrap();
|
||||
// We autofill from A3 to A5
|
||||
model
|
||||
.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 3,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
5,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 4, 1),
|
||||
Ok("alpha".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 5, 1),
|
||||
Ok("alpha".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_cell_down() {
|
||||
let model = new_empty_model();
|
||||
let mut model = UserModel::from_model(model);
|
||||
model.set_user_input(0, 1, 1, "23").unwrap();
|
||||
model
|
||||
.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 1,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
2,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 2, 1),
|
||||
Ok("23".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alpha_beta_gamma() {
|
||||
let model = new_empty_model();
|
||||
let mut model = UserModel::from_model(model);
|
||||
// cells A1:B3
|
||||
model.set_user_input(0, 1, 1, "Alpher").unwrap();
|
||||
model.set_user_input(0, 2, 1, "Bethe").unwrap();
|
||||
model.set_user_input(0, 3, 1, "Gamow").unwrap();
|
||||
model.set_user_input(0, 1, 2, "=A1").unwrap();
|
||||
model.set_user_input(0, 2, 2, "=A2").unwrap();
|
||||
model.set_user_input(0, 3, 2, "=A3").unwrap();
|
||||
// We autofill from A1:B3 to A9
|
||||
model
|
||||
.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 1,
|
||||
column: 1,
|
||||
width: 2,
|
||||
height: 3,
|
||||
},
|
||||
9,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 4, 1),
|
||||
Ok("Alpher".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 5, 1),
|
||||
Ok("Bethe".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 6, 1),
|
||||
Ok("Gamow".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 7, 1),
|
||||
Ok("Alpher".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 8, 1),
|
||||
Ok("Bethe".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 9, 1),
|
||||
Ok("Gamow".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 4, 2),
|
||||
Ok("Alpher".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 5, 2),
|
||||
Ok("Bethe".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 6, 2),
|
||||
Ok("Gamow".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 7, 2),
|
||||
Ok("Alpher".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 8, 2),
|
||||
Ok("Bethe".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 9, 2),
|
||||
Ok("Gamow".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(model.get_cell_content(0, 4, 2), Ok("=A4".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn styles() {
|
||||
let model = new_empty_model();
|
||||
let mut model = UserModel::from_model(model);
|
||||
// cells A1:B3
|
||||
model.set_user_input(0, 1, 1, "Alpher").unwrap();
|
||||
model.set_user_input(0, 2, 1, "Bethe").unwrap();
|
||||
model.set_user_input(0, 3, 1, "Gamow").unwrap();
|
||||
|
||||
let a2 = Area {
|
||||
sheet: 0,
|
||||
row: 2,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
|
||||
let a3 = Area {
|
||||
sheet: 0,
|
||||
row: 3,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
};
|
||||
|
||||
model.update_range_style(&a2, "font.i", "true").unwrap();
|
||||
model
|
||||
.update_range_style(&a3, "fill.bg_color", "#334455")
|
||||
.unwrap();
|
||||
|
||||
model
|
||||
.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 1,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 3,
|
||||
},
|
||||
9,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Check that cell A5 has A2 style
|
||||
let style = model.get_cell_style(0, 5, 1).unwrap();
|
||||
assert!(style.font.i);
|
||||
// A6 would have the style of A3
|
||||
let style = model.get_cell_style(0, 6, 1).unwrap();
|
||||
assert_eq!(style.fill.bg_color, Some("#334455".to_string()));
|
||||
|
||||
model.undo().unwrap();
|
||||
|
||||
assert_eq!(model.get_cell_content(0, 4, 1), Ok("".to_string()));
|
||||
// Check that cell A5 has A2 style
|
||||
let style = model.get_cell_style(0, 5, 1).unwrap();
|
||||
assert!(!style.font.i);
|
||||
// A6 would have the style of A3
|
||||
let style = model.get_cell_style(0, 6, 1).unwrap();
|
||||
assert_eq!(style.fill.bg_color, None);
|
||||
|
||||
model.redo().unwrap();
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 4, 1),
|
||||
Ok("Alpher".to_string())
|
||||
);
|
||||
// Check that cell A5 has A2 style
|
||||
let style = model.get_cell_style(0, 5, 1).unwrap();
|
||||
assert!(style.font.i);
|
||||
// A6 would have the style of A3
|
||||
let style = model.get_cell_style(0, 6, 1).unwrap();
|
||||
assert_eq!(style.fill.bg_color, Some("#334455".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upwards() {
|
||||
let model = new_empty_model();
|
||||
let mut model = UserModel::from_model(model);
|
||||
// cells A10:A12
|
||||
model.set_user_input(0, 10, 1, "Alpher").unwrap();
|
||||
model.set_user_input(0, 11, 1, "Bethe").unwrap();
|
||||
model.set_user_input(0, 12, 1, "Gamow").unwrap();
|
||||
|
||||
// We fill upwards to row 5
|
||||
model
|
||||
.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 10,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 3,
|
||||
},
|
||||
5,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 9, 1),
|
||||
Ok("Gamow".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 8, 1),
|
||||
Ok("Bethe".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 7, 1),
|
||||
Ok("Alpher".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upwards_4() {
|
||||
let model = new_empty_model();
|
||||
let mut model = UserModel::from_model(model);
|
||||
// cells A10:A13
|
||||
model.set_user_input(0, 10, 1, "Margaret Burbidge").unwrap();
|
||||
model.set_user_input(0, 11, 1, "Geoffrey Burbidge").unwrap();
|
||||
model.set_user_input(0, 12, 1, "Willy Fowler").unwrap();
|
||||
model.set_user_input(0, 13, 1, "Fred Hoyle").unwrap();
|
||||
|
||||
// We fill upwards to row 5
|
||||
model
|
||||
.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 10,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 4,
|
||||
},
|
||||
5,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 9, 1),
|
||||
Ok("Fred Hoyle".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 8, 1),
|
||||
Ok("Willy Fowler".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 5, 1),
|
||||
Ok("Fred Hoyle".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errors() {
|
||||
let model = new_empty_model();
|
||||
let mut model = UserModel::from_model(model);
|
||||
// cells A10:A13
|
||||
model.set_user_input(0, 4, 1, "Margaret Burbidge").unwrap();
|
||||
|
||||
// Invalid sheet
|
||||
assert_eq!(
|
||||
model.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 3,
|
||||
row: 4,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
10,
|
||||
),
|
||||
Err("Invalid worksheet index: '3'".to_string())
|
||||
);
|
||||
|
||||
// invalid row
|
||||
assert_eq!(
|
||||
model.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: -1,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
10,
|
||||
),
|
||||
Err("Invalid row: '-1'".to_string())
|
||||
);
|
||||
|
||||
// invalid row
|
||||
assert_eq!(
|
||||
model.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: LAST_ROW - 1,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 10,
|
||||
},
|
||||
10,
|
||||
),
|
||||
Err("Invalid row: '1048584'".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
model.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 1,
|
||||
column: LAST_COLUMN + 1,
|
||||
width: 1,
|
||||
height: 10,
|
||||
},
|
||||
10,
|
||||
),
|
||||
Err("Invalid column: '16385'".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
model.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 1,
|
||||
column: LAST_COLUMN - 2,
|
||||
width: 10,
|
||||
height: 1,
|
||||
},
|
||||
10,
|
||||
),
|
||||
Err("Invalid column: '16391'".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
model.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 5,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 10,
|
||||
},
|
||||
-10,
|
||||
),
|
||||
Err("Invalid row: '-10'".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_parameters() {
|
||||
let model = new_empty_model();
|
||||
let mut model = UserModel::from_model(model);
|
||||
model.set_user_input(0, 1, 1, "23").unwrap();
|
||||
assert_eq!(
|
||||
model.auto_fill_rows(
|
||||
&Area {
|
||||
sheet: 0,
|
||||
row: 1,
|
||||
column: 1,
|
||||
width: 1,
|
||||
height: 2,
|
||||
},
|
||||
2,
|
||||
),
|
||||
Err("Invalid parameters for autofill".to_string())
|
||||
);
|
||||
}
|
||||
@@ -927,7 +927,7 @@ impl UserModel {
|
||||
column,
|
||||
old_value: Box::new(old_value),
|
||||
new_value: Box::new(style),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
self.push_diff_list(diff_list);
|
||||
@@ -943,6 +943,107 @@ impl UserModel {
|
||||
Ok(self.model.get_style_for_cell(sheet, row, column))
|
||||
}
|
||||
|
||||
/// Fills the cells from `source_area` until `to_row`.
|
||||
/// This simulates the user clicking on the cell outline handle and dragging it downwards (or upwards)
|
||||
pub fn auto_fill_rows(&mut self, source_area: &Area, to_row: i32) -> Result<(), String> {
|
||||
let mut diff_list = Vec::new();
|
||||
let sheet = source_area.sheet;
|
||||
let row1 = source_area.row;
|
||||
let column1 = source_area.column;
|
||||
let width1 = source_area.width;
|
||||
let height1 = source_area.height;
|
||||
|
||||
// Check first all parameters are valid
|
||||
if self.model.workbook.worksheet(sheet).is_err() {
|
||||
return Err(format!("Invalid worksheet index: '{sheet}'"));
|
||||
}
|
||||
|
||||
if !is_valid_column_number(column1) {
|
||||
return Err(format!("Invalid column: '{column1}'"));
|
||||
}
|
||||
if !is_valid_row(row1) {
|
||||
return Err(format!("Invalid row: '{row1}'"));
|
||||
}
|
||||
if !is_valid_column_number(column1 + width1 - 1) {
|
||||
return Err(format!("Invalid column: '{}'", column1 + width1 - 1));
|
||||
}
|
||||
if !is_valid_row(row1 + height1 - 1) {
|
||||
return Err(format!("Invalid row: '{}'", row1 + height1 - 1));
|
||||
}
|
||||
|
||||
if !is_valid_row(to_row) {
|
||||
return Err(format!("Invalid row: '{to_row}'"));
|
||||
}
|
||||
|
||||
// anchor_row is the first row that repeats in each case.
|
||||
let anchor_row;
|
||||
let sign;
|
||||
// this is the range of rows we are going to fill
|
||||
let row_range: Vec<i32>;
|
||||
|
||||
if to_row >= row1 + height1 {
|
||||
// we go downwards, we start from `row1 + height1` to `to_row`,
|
||||
anchor_row = row1;
|
||||
sign = 1;
|
||||
row_range = (row1 + height1..to_row + 1).collect();
|
||||
} else if to_row < row1 {
|
||||
// we go upwards, starting from `row1 - `` all the way to `to_row`
|
||||
anchor_row = row1 + height1 - 1;
|
||||
sign = -1;
|
||||
row_range = (to_row..row1).rev().collect();
|
||||
} else {
|
||||
return Err("Invalid parameters for autofill".to_string());
|
||||
}
|
||||
|
||||
for column in column1..column1 + width1 {
|
||||
let mut index = 0;
|
||||
for row_ref in &row_range {
|
||||
// Save value and style first
|
||||
let row = *row_ref;
|
||||
let old_value = self
|
||||
.model
|
||||
.workbook
|
||||
.worksheet(sheet)?
|
||||
.cell(row, column)
|
||||
.cloned();
|
||||
let old_style = self.model.get_style_for_cell(sheet, row, column);
|
||||
|
||||
// compute the new value and set it
|
||||
let source_row = anchor_row + index;
|
||||
let target_value = self
|
||||
.model
|
||||
.extend_to(sheet, source_row, column, row, column)?;
|
||||
self.model
|
||||
.set_user_input(sheet, row, column, target_value.to_string());
|
||||
|
||||
// Compute the new style and set it
|
||||
let new_style = self.model.get_style_for_cell(sheet, source_row, column);
|
||||
self.model.set_cell_style(sheet, row, column, &new_style)?;
|
||||
|
||||
// Add the diffs
|
||||
diff_list.push(Diff::SetCellStyle {
|
||||
sheet,
|
||||
row,
|
||||
column,
|
||||
old_value: Box::new(old_style),
|
||||
new_value: Box::new(new_style),
|
||||
});
|
||||
diff_list.push(Diff::SetCellValue {
|
||||
sheet,
|
||||
row,
|
||||
column,
|
||||
new_value: target_value.to_string(),
|
||||
old_value: Box::new(old_value),
|
||||
});
|
||||
|
||||
index = (index + sign) % height1;
|
||||
}
|
||||
}
|
||||
self.push_diff_list(diff_list);
|
||||
self.evaluate();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns information about the sheets
|
||||
///
|
||||
/// See also:
|
||||
@@ -1028,7 +1129,7 @@ impl UserModel {
|
||||
if !is_valid_column_number(column) {
|
||||
return Err(format!("Invalid column: '{column}'"));
|
||||
}
|
||||
if !is_valid_column_number(row) {
|
||||
if !is_valid_row(row) {
|
||||
return Err(format!("Invalid row: '{row}'"));
|
||||
}
|
||||
if self.model.workbook.worksheet(sheet).is_err() {
|
||||
|
||||
Reference in New Issue
Block a user