Compare commits
1 Commits
feature-ni
...
feature/ni
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7ce9f5ec9 |
@@ -418,7 +418,6 @@ impl Model {
|
||||
CalcResult::new_error(Error::NIMPL, cell, "Arrays not implemented".to_string())
|
||||
}
|
||||
VariableKind(defined_name) => {
|
||||
println!("{:?}", defined_name);
|
||||
let parsed_defined_name = self
|
||||
.parsed_defined_names
|
||||
.get(&(Some(cell.sheet), defined_name.to_lowercase())) // try getting local defined name
|
||||
@@ -427,11 +426,9 @@ impl Model {
|
||||
.get(&(None, defined_name.to_lowercase()))
|
||||
}); // fallback to global
|
||||
|
||||
println!("Parsed: {:?}", defined_name);
|
||||
if let Some(parsed_defined_name) = parsed_defined_name {
|
||||
match parsed_defined_name {
|
||||
ParsedDefinedName::CellReference(reference) => {
|
||||
println!("Here: {}", reference);
|
||||
self.evaluate_cell(*reference)
|
||||
}
|
||||
ParsedDefinedName::RangeReference(range) => CalcResult::Range {
|
||||
@@ -1989,95 +1986,6 @@ impl Model {
|
||||
.worksheet_mut(sheet)?
|
||||
.set_row_height(column, height)
|
||||
}
|
||||
|
||||
/// Adds a new defined name
|
||||
pub fn new_defined_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
scope: Option<u32>,
|
||||
formula: &str,
|
||||
) -> Result<(), String> {
|
||||
let name_upper = name.to_uppercase();
|
||||
let defined_names = &self.workbook.defined_names;
|
||||
// if the defined name already exist return error
|
||||
for df in defined_names {
|
||||
if df.name.to_uppercase() == name_upper && df.sheet_id == scope {
|
||||
return Err("Defined name already exists".to_string());
|
||||
}
|
||||
}
|
||||
self.workbook.defined_names.push(DefinedName {
|
||||
name: name.to_string(),
|
||||
formula: formula.to_string(),
|
||||
sheet_id: scope,
|
||||
});
|
||||
self.reset_parsed_structures();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete defined name of name and scope
|
||||
pub fn delete_defined_name(&mut self, name: &str, scope: Option<u32>) -> Result<(), String> {
|
||||
let name_upper = name.to_uppercase();
|
||||
let defined_names = &self.workbook.defined_names;
|
||||
let mut index = None;
|
||||
for (i, df) in defined_names.iter().enumerate() {
|
||||
if df.name.to_uppercase() == name_upper && df.sheet_id == scope {
|
||||
index = Some(i);
|
||||
}
|
||||
}
|
||||
if let Some(i) = index {
|
||||
self.workbook.defined_names.remove(i);
|
||||
self.reset_parsed_structures();
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Defined name not found".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the formula for a defined name
|
||||
pub fn get_defined_name_formula(
|
||||
&self,
|
||||
name: &str,
|
||||
scope: Option<u32>,
|
||||
) -> Result<String, String> {
|
||||
let name_upper = name.to_uppercase();
|
||||
let defined_names = &self.workbook.defined_names;
|
||||
for df in defined_names {
|
||||
if df.name.to_uppercase() == name_upper && df.sheet_id == scope {
|
||||
return Ok(df.formula.clone());
|
||||
}
|
||||
}
|
||||
Err("Defined name not found".to_string())
|
||||
}
|
||||
|
||||
/// update defined name
|
||||
pub fn update_defined_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
scope: Option<u32>,
|
||||
new_name: &str,
|
||||
new_scope: Option<u32>,
|
||||
new_formula: &str,
|
||||
) -> Result<(), String> {
|
||||
let name_upper = name.to_uppercase();
|
||||
let defined_names = &self.workbook.defined_names;
|
||||
let mut index = None;
|
||||
for (i, df) in defined_names.iter().enumerate() {
|
||||
if df.name.to_uppercase() == name_upper && df.sheet_id == scope {
|
||||
index = Some(i);
|
||||
}
|
||||
}
|
||||
if let Some(i) = index {
|
||||
if let Some(df) = self.workbook.defined_names.get_mut(i) {
|
||||
df.name = new_name.to_string();
|
||||
df.sheet_id = new_scope;
|
||||
df.formula = new_formula.to_string();
|
||||
self.reset_parsed_structures();
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Defined name not found".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -68,6 +68,7 @@ impl Model {
|
||||
frozen_rows: 0,
|
||||
show_grid_lines: true,
|
||||
views,
|
||||
conditional_formatting: vec![]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ mod test_autofill_columns;
|
||||
mod test_autofill_rows;
|
||||
mod test_border;
|
||||
mod test_clear_cells;
|
||||
mod test_defined_names;
|
||||
mod test_diff_queue;
|
||||
mod test_evaluation;
|
||||
mod test_general;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use crate::UserModel;
|
||||
|
||||
#[test]
|
||||
fn create_defined_name() {
|
||||
let mut model = UserModel::new_empty("model", "en", "UTC").unwrap();
|
||||
model.set_user_input(0, 1, 1, "42").unwrap();
|
||||
model.new_defined_name("myName", None, "$A$1").unwrap();
|
||||
model.set_user_input(0, 5, 7, "=myName").unwrap();
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 5, 7),
|
||||
Ok("42".to_string())
|
||||
);
|
||||
|
||||
// rename it
|
||||
model
|
||||
.update_defined_name("myName", None, "myName", None, "$A$1*2")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 5, 7),
|
||||
Ok("42".to_string())
|
||||
);
|
||||
|
||||
// delete it
|
||||
model.delete_defined_name("myName", None).unwrap();
|
||||
assert_eq!(
|
||||
model.get_formatted_cell_value(0, 5, 7),
|
||||
Ok("#REF!".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_defined_name() {}
|
||||
|
||||
#[test]
|
||||
fn delete_sheet() {}
|
||||
|
||||
#[test]
|
||||
fn change_scope() {}
|
||||
@@ -117,6 +117,26 @@ pub struct Worksheet {
|
||||
pub views: HashMap<u32, WorksheetView>,
|
||||
/// Whether or not to show the grid lines in the worksheet
|
||||
pub show_grid_lines: bool,
|
||||
pub conditional_formatting: Vec<ConditionalFormatting>
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
|
||||
pub struct ColorScale {
|
||||
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
|
||||
pub enum CfRule {
|
||||
ColorScale {
|
||||
priority: u32,
|
||||
},
|
||||
CellIs,
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, Debug, PartialEq, Clone)]
|
||||
pub struct ConditionalFormatting {
|
||||
sqref: String,
|
||||
cf_rule: Vec<CfRule>
|
||||
}
|
||||
|
||||
/// Internal representation of Excel's sheet_data
|
||||
|
||||
@@ -13,8 +13,8 @@ use crate::{
|
||||
},
|
||||
model::Model,
|
||||
types::{
|
||||
Alignment, BorderItem, CellType, Col, DefinedName, HorizontalAlignment, SheetProperties,
|
||||
Style, VerticalAlignment,
|
||||
Alignment, BorderItem, CellType, Col, HorizontalAlignment, SheetProperties, Style,
|
||||
VerticalAlignment,
|
||||
},
|
||||
utils::is_valid_hex_color,
|
||||
};
|
||||
@@ -1689,66 +1689,6 @@ impl UserModel {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the list of defined names
|
||||
pub fn get_defined_name_list(&self) -> Vec<DefinedName> {
|
||||
self.model.workbook.defined_names.clone()
|
||||
}
|
||||
|
||||
/// Delete an existing defined name
|
||||
pub fn delete_defined_name(&mut self, name: &str, scope: Option<u32>) -> Result<(), String> {
|
||||
let old_value = self.model.get_defined_name_formula(name, scope)?;
|
||||
let diff_list = vec![Diff::DeleteDefinedName {
|
||||
name: name.to_string(),
|
||||
scope,
|
||||
old_value,
|
||||
}];
|
||||
self.push_diff_list(diff_list);
|
||||
self.model.delete_defined_name(name, scope)?;
|
||||
self.evaluate_if_not_paused();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a new defined name
|
||||
pub fn new_defined_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
scope: Option<u32>,
|
||||
formula: &str,
|
||||
) -> Result<(), String> {
|
||||
self.model.new_defined_name(name, scope, formula)?;
|
||||
let diff_list = vec![Diff::CreateDefinedName {
|
||||
name: name.to_string(),
|
||||
scope,
|
||||
value: formula.to_string(),
|
||||
}];
|
||||
self.push_diff_list(diff_list);
|
||||
self.evaluate_if_not_paused();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates a defined name
|
||||
pub fn update_defined_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
scope: Option<u32>,
|
||||
new_name: &str,
|
||||
new_scope: Option<u32>,
|
||||
new_formula: &str,
|
||||
) -> Result<(), String> {
|
||||
let old_formula = self.model.get_defined_name_formula(name, scope)?;
|
||||
let diff_list = vec![Diff::UpdateDefinedName {
|
||||
name: name.to_string(),
|
||||
scope,
|
||||
old_formula: old_formula.to_string(),
|
||||
new_name: new_name.to_string(),
|
||||
new_scope,
|
||||
new_formula: new_formula.to_string(),
|
||||
}];
|
||||
self.push_diff_list(diff_list);
|
||||
self.evaluate_if_not_paused();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// **** Private methods ****** //
|
||||
|
||||
fn push_diff_list(&mut self, diff_list: DiffList) {
|
||||
@@ -1919,20 +1859,6 @@ impl UserModel {
|
||||
} => {
|
||||
self.model.set_show_grid_lines(*sheet, *old_value)?;
|
||||
}
|
||||
Diff::CreateDefinedName { name, scope, value } => todo!(),
|
||||
Diff::DeleteDefinedName {
|
||||
name,
|
||||
scope,
|
||||
old_value,
|
||||
} => todo!(),
|
||||
Diff::UpdateDefinedName {
|
||||
name,
|
||||
scope,
|
||||
old_formula,
|
||||
new_name,
|
||||
new_scope,
|
||||
new_formula,
|
||||
} => todo!(),
|
||||
}
|
||||
}
|
||||
if needs_evaluation {
|
||||
@@ -2060,20 +1986,6 @@ impl UserModel {
|
||||
} => {
|
||||
self.model.set_show_grid_lines(*sheet, *new_value)?;
|
||||
}
|
||||
Diff::CreateDefinedName { name, scope, value } => todo!(),
|
||||
Diff::DeleteDefinedName {
|
||||
name,
|
||||
scope,
|
||||
old_value,
|
||||
} => todo!(),
|
||||
Diff::UpdateDefinedName {
|
||||
name,
|
||||
scope,
|
||||
old_formula,
|
||||
new_name,
|
||||
new_scope,
|
||||
new_formula,
|
||||
} => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,26 +108,7 @@ pub(crate) enum Diff {
|
||||
sheet: u32,
|
||||
old_value: bool,
|
||||
new_value: bool,
|
||||
},
|
||||
CreateDefinedName {
|
||||
name: String,
|
||||
scope: Option<u32>,
|
||||
value: String,
|
||||
},
|
||||
DeleteDefinedName {
|
||||
name: String,
|
||||
scope: Option<u32>,
|
||||
old_value: String,
|
||||
},
|
||||
UpdateDefinedName {
|
||||
name: String,
|
||||
scope: Option<u32>,
|
||||
old_formula: String,
|
||||
new_name: String,
|
||||
new_scope: Option<u32>,
|
||||
new_formula: String,
|
||||
},
|
||||
// FIXME: we are missing SetViewDiffs
|
||||
}, // FIXME: we are missing SetViewDiffs
|
||||
}
|
||||
|
||||
pub(crate) type DiffList = Vec<Diff>;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use serde::Serialize;
|
||||
use wasm_bindgen::{
|
||||
prelude::{wasm_bindgen, JsError},
|
||||
JsValue,
|
||||
@@ -30,13 +29,6 @@ pub fn column_name_from_number(column: i32) -> Result<String, JsError> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct DefinedName {
|
||||
name: String,
|
||||
scope: Option<u32>,
|
||||
formula: String,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Model {
|
||||
model: BaseModel,
|
||||
@@ -549,43 +541,4 @@ impl Model {
|
||||
.paste_csv_string(&range, csv)
|
||||
.map_err(|e| to_js_error(e.to_string()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "getDefinedNameList")]
|
||||
pub fn get_defined_name_list(&self) -> Result<JsValue, JsError> {
|
||||
let data: DefinedNameList =
|
||||
self.model
|
||||
.get_defined_name_list()
|
||||
.iter()
|
||||
.map(|s| DefinedName {
|
||||
name: s.name.to_string(),
|
||||
scope: s.sheet_id,
|
||||
formula: s.formula.to_string(),
|
||||
});
|
||||
// Ok(data)
|
||||
serde_wasm_bindgen::to_value(&data).map_err(|e| to_js_error(e.to_string()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "newDefinedName")]
|
||||
pub fn new_defined_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
scope: Option<u32>,
|
||||
formula: &str,
|
||||
) -> Result<(), JsError> {
|
||||
self.model
|
||||
.new_defined_name(name, scope, formula)
|
||||
.map_err(|e| to_js_error(e.to_string()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "updateDefinedName")]
|
||||
pub fn update_defined_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
scope: Option<u32>,
|
||||
new_name: &str,
|
||||
new_scope: Option<u32>,
|
||||
new_formula: &str,
|
||||
) -> Result<(), JsError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,65 @@ fn load_columns(ws: Node) -> Result<Vec<Col>, XlsxError> {
|
||||
Ok(cols)
|
||||
}
|
||||
|
||||
|
||||
// https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_cfRule_topic_ID0EFKO4.html
|
||||
fn load_conditional_formatting(ws: Node) -> Result<Vec<String>, XlsxError> {
|
||||
// Conditional Formatting
|
||||
// <conditionalFormatting sqref="B1:B9">
|
||||
// <cfRule type="colorScale" priority="1">
|
||||
// <colorScale>
|
||||
// <cfvo type="min"/>
|
||||
// <cfvo type="max"/>
|
||||
// <color rgb="FFF8696B"/>
|
||||
// <color rgb="FFFCFCFF"/>
|
||||
// </colorScale>
|
||||
// </cfRule>
|
||||
// </conditionalFormatting>
|
||||
let mut conditional_formatting = Vec::new();
|
||||
let conditional_formatting_nodes = ws
|
||||
.children()
|
||||
.filter(|n| n.has_tag_name("conditionalFormattng"))
|
||||
.collect::<Vec<Node>>();
|
||||
for format in conditional_formatting_nodes {
|
||||
let reference = get_attribute(&format, "sqref")?.to_string();
|
||||
// cfRule
|
||||
let cf_rule_nodes = ws
|
||||
.children()
|
||||
.filter(|n| n.has_tag_name("cfRule"))
|
||||
.collect::<Vec<Node>>();
|
||||
if cf_rule_nodes.len() == 1 {
|
||||
let cf_rule = cf_rule_nodes[0] ;
|
||||
let cf_type = get_attribute(&cf_rule, "type")?.to_string();
|
||||
match cf_type.as_str() {
|
||||
"colorScale" => {
|
||||
todo!()
|
||||
}
|
||||
"aboveAverage" => {
|
||||
let dxf_id = get_attribute(&cf_rule, "dxfId")?.to_string();
|
||||
todo!()
|
||||
}
|
||||
"notContainsBlanks" => {
|
||||
let dxf_id = get_attribute(&cf_rule, "dxfId")?.to_string();
|
||||
|
||||
}
|
||||
_ => {}
|
||||
|
||||
}
|
||||
}
|
||||
// priority
|
||||
|
||||
// if type is avobeAverage then avobeAverage (and equlAverage)
|
||||
|
||||
// dxfId for some types((Differential Formatting Id) What style to apply when the criteria are met
|
||||
|
||||
// Posible children
|
||||
// formula
|
||||
// colorScales
|
||||
}
|
||||
|
||||
Ok(conditional_formatting)
|
||||
}
|
||||
|
||||
fn load_merge_cells(ws: Node) -> Result<Vec<String>, XlsxError> {
|
||||
// 18.3.1.55 Merge Cells
|
||||
// <mergeCells count="1">
|
||||
@@ -945,17 +1004,7 @@ pub(super) fn load_sheet<R: Read + std::io::Seek>(
|
||||
|
||||
let merge_cells = load_merge_cells(ws)?;
|
||||
|
||||
// Conditional Formatting
|
||||
// <conditionalFormatting sqref="B1:B9">
|
||||
// <cfRule type="colorScale" priority="1">
|
||||
// <colorScale>
|
||||
// <cfvo type="min"/>
|
||||
// <cfvo type="max"/>
|
||||
// <color rgb="FFF8696B"/>
|
||||
// <color rgb="FFFCFCFF"/>
|
||||
// </colorScale>
|
||||
// </cfRule>
|
||||
// </conditionalFormatting>
|
||||
let conditional_formatting = load_conditional_formatting(ws)?;
|
||||
// pageSetup
|
||||
// <pageSetup orientation="portrait" r:id="rId1"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user