diff --git a/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas b/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas
index 98da5107e..e6291cc3e 100644
--- a/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas
+++ b/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas
@@ -26,7 +26,7 @@ begin
sh.WriteText(0, 2, 'Test values');
row := 2;
- for i := row to row+30 do
+ for i := row to row+33 do
begin
sh.WriteNumber(i, 2, 1.0);
sh.WriteNumber(i, 3, 2.0);
@@ -324,6 +324,12 @@ begin
sh.WriteText(row, 1, 'yellow -> red');
sh.WriteColorRange(Range(Row, 2, row, 12), scYellow, scRed);
+ // Icon sets
+ inc(row);
+ sh.WriteText(row, 0, 'IconSet');
+ sh.WriteText(row, 1, '3 flags');
+ sh.WriteIconSet(Range(Row, 2, row, 12), is3Flags);
+
{ ------ Save workbook to file-------------------------------------------- }
wb.WriteToFile('test.xlsx', true);
wb.WriteToFile('test.ods', true);
diff --git a/components/fpspreadsheet/source/common/fpsconditionalformat.pas b/components/fpspreadsheet/source/common/fpsconditionalformat.pas
index 65c1f01dd..271ca0ea3 100644
--- a/components/fpspreadsheet/source/common/fpsconditionalformat.pas
+++ b/components/fpspreadsheet/source/common/fpsconditionalformat.pas
@@ -68,6 +68,41 @@ type
procedure Assign(ASource: TsCFRule); override;
end;
+ { Icon sets }
+ TsCFIconSet = (
+ is3Arrows, is3ArrowsGray, is3Flags,
+ is3TrafficLights1, // x14 in xlsx
+ is3TrafficLights2, is3Signs, is3Symbols, is3Symbols2,
+ is3Smilies, is3Stars, is3Triangles, is3ColorSmilies, // need x14 in xlsx
+ is4Arrows, is4ArrowsGray, is4RedToBlack, is4Rating,
+ is4TrafficLights, // not in ODS
+ is5Arrows, is5ArrowsGray, is5Rating, is5Quarters,
+ is5Boxes // needs x14 in Excel
+ );
+ TsCFIconSetRule = class(TsCFRule)
+ private
+ FIconSet: TsCFIconSet;
+ FReverse: Boolean;
+ FShowValue: Boolean;
+ FValueKinds: array of TsCFValuekind;
+ FValues: array of double;
+ function GetIconCount: Integer;
+ function GetValueKinds(AIndex: Integer): TsCFValueKind;
+ function GetValues(AIndex: Integer): Double;
+ procedure SetIconSet(AValue: TsCFIconSet);
+ procedure SetValueKinds(AIndex: Integer; AKind: TsCFValueKind);
+ procedure SetValues(AIndex: Integer; AValue: Double);
+ public
+ constructor Create;
+ procedure Assign(ASource: TsCFRule); override;
+ property IconSet: TsCFIconSet read FIconSet write SetIconSet;
+ property IconCount: Integer read GetIconCount;
+ property Values[AIndex: Integer]: Double read GetValues write SetValues;
+ property ValueKinds[AIndex: Integer]: TsCFValueKind read GetValueKinds write SetValueKinds;
+ property Reverse: Boolean read FReverse write FReverse;
+ property ShowValue: Boolean read FShowValue write FShowValue;
+ end;
+
{ Rules }
TsCFRules = class(TFPObjectList)
private
@@ -127,17 +162,44 @@ type
ABarColor: TsColor; AStartKind: TsCFValueKind; AStartValue: Double;
AEndKind: TsCFValueKind; AEndValue: Double): Integer; overload;
+ function AddIconSetRule(ASheet: TsBasicWorksheet; ARange: TsCellRange;
+ AIconSet: TsCFIconSet; AHideValue: Boolean = false; AReverse: Boolean = false): Integer; overload;
+ function AddIconSetRule(ASheet: TsBasicWorksheet; ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AHideValue: Boolean = false; AReverse: Boolean = false): Integer; overload;
+ function AddIconSetRule(ASheet: TsBasicWorksheet; ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AValueKind3: TsCFValueKind; AValue3: Double;
+ AHideValue: Boolean = false; AReverse: Boolean = false): Integer; overload;
+ function AddIconSetRule(ASheet: TsBasicWorksheet; ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AValueKind3: TsCFValueKind; AValue3: Double;
+ AValueKind4: TsCFValueKind; AValue4: Double;
+ AHideValue: Boolean = false; AReverse: Boolean = false): Integer; overload;
+
procedure Delete(AIndex: Integer);
function Find(ASheet: TsBasicWorksheet; ARange: TsCellRange): Integer;
end;
+ function GetCFIconCount(AIconSet: TsCFIconSet): Integer;
implementation
uses
- Math,
+ Math, TypInfo,
fpSpreadsheet;
+function GetCFIconCount(AIconSet: TsCFIconSet): Integer;
+var
+ s: String;
+begin
+ s := GetEnumName(TypeInfo(TsCFIconSet), integer(AIconSet));
+ Result := ord(s[3]) - ord('0');
+end;
+
procedure TsCFCellRule.Assign(ASource: TsCFRule);
begin
if ASource is TsCFCellRule then
@@ -164,7 +226,7 @@ begin
begin
//
end else
- raise Exception.Create('Source cannot be assigned to TCVDataBarRule');
+ raise Exception.Create('Source cannot be assigned to TsCFDataBarRule');
end;
constructor TsCFColorRangeRule.Create;
@@ -194,7 +256,7 @@ begin
CenterColor := TsCFColorRangeRule(ASource).CenterColor;
EndColor := TsCFColorRangeRule(ASource).EndColor;
end else
- raise Exception.Create('Source cannot be assigned to TCVDataBarRule');
+ raise Exception.Create('Source cannot be assigned to TsCFColorRangeRule');
end;
procedure TsCFColorRangeRule.SetupCenter(AColor: TsColor;
@@ -222,8 +284,89 @@ begin
end;
+{ TsCFIconSetRule }
-{ TCFRule }
+constructor TsCFIconSetRule.Create;
+begin
+ FIconSet := is3Arrows;
+ SetLength(FValues, 2);
+ Setlength(FValueKinds, 2);
+ FValues[0] := 33;
+ FValues[1] := 66;
+ FValueKinds[0] := vkPercent;
+ FValueKinds[1] := vkPercent;
+ FShowValue := true;
+ FReverse := false;
+end;
+
+procedure TsCFIconSetRule.Assign(ASource: TsCFRule);
+var
+ i: Integer;
+begin
+ if ASource is TsCFIconSetRule then
+ begin
+ SetIconSet(TsCFIconSetRule(ASource).IconSet);
+ for i := 0 to High(FValues) do
+ FValues[i] := TsCFIconSetRule(ASource).Values[i];
+ for i := 0 to High(FValueKinds) do
+ FValueKinds[i] := TsCFIconSetRule(ASource).ValueKinds[i];
+ FShowValue := TsCFIconSetRule(ASource).ShowValue;
+ FReverse := TsCFIconSetRule(ASource).Reverse;
+ end else
+ raise Exception.Create('Source cannot be assigned to TsCFIconSetRule');
+end;
+
+function TsCFIconSetRule.GetIconCount: Integer;
+begin
+ Result := Length(FValues) + 1;
+end;
+
+function TsCFIconSetRule.GetValueKinds(AIndex: Integer): TsCFValueKind;
+begin
+ Result := FValueKinds[AIndex];
+end;
+
+function TsCFIconSetRule.GetValues(AIndex: Integer): Double;
+begin
+ Result := FValues[AIndex];
+end;
+
+procedure TsCFIconSetRule.SetIconSet(AValue: TsCFIconSet);
+var
+ s: String;
+ i, n: Integer;
+begin
+ if AValue = FIconSet then exit;
+
+ FIconSet := AValue;
+
+ s := GetEnumName(TypeInfo(TsCFIconSet), integer(AValue));
+ n := Ord(s[3]) - ord('0');
+ SetLength(FValues, n - 1);
+ for i := 0 to High(FValues) do
+ FValues[i] := (i + 1) * 100 div n;
+ SetLength(FValueKinds, n - 1);
+ for i := 0 to High(FValueKinds) do
+ FValueKinds[i] := vkPercent;
+
+ // value index
+ // (min) 0 1 2 (max)
+ // |---------|---------|---------|----------|
+ // icon0 icon1 icon2 icon3
+end;
+
+procedure TsCFIconSetRule.SetValueKinds(AIndex: Integer; AKind: TsCFValueKind);
+begin
+ FValueKinds[AIndex] := AKind;
+end;
+
+procedure TsCFIconSetRule.SetValues(AIndex: Integer; AValue: Double);
+begin
+ FValues[AIndex] := AValue;
+end;
+
+
+{ TsCFRule }
function TsCFRules.GetItem(AIndex: Integer): TsCFRule;
begin
@@ -424,6 +567,115 @@ begin
end;
+function TsConditionalFormatList.AddIconSetRule(ASheet: TsBasicWorksheet;
+ ARange: TsCellRange; AIconSet: TsCFIconSet; AHideValue: Boolean = false;
+ AReverse: Boolean = false): Integer;
+var
+ rule: TsCFIconSetRule;
+ i, n: Integer;
+begin
+ rule := TsCFIconSetRule.Create;
+ rule.IconSet := AIconset;
+ n := rule.IconCount;
+ for i := 0 to n - 2 do
+ begin
+ rule.ValueKinds[i] := vkPercent;
+ rule.Values[i] := ((i+1) * 100) div n
+ end;
+ rule.ShowValue := not AHideValue;
+ rule.Reverse := AReverse;
+ Result := AddRule(ASheet, ARange, rule);
+end;
+
+{ IconSet conditional format for 3 icons, ie. 2 values }
+function TsConditionalFormatList.AddIconSetRule(ASheet: TsBasicWorksheet;
+ ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AHideValue: Boolean = false; AReverse: Boolean = false): Integer;
+var
+ rule: TsCFIconSetRule;
+ n: Integer;
+begin
+ rule := TsCFIconSetRule.Create;
+ rule.IconSet := AIconset;
+ n := rule.IconCount;
+ if n <> 3 then begin
+ rule.Free;
+ Result := -1;
+ exit;
+ end;
+
+ rule.ValueKinds[0] := AValueKind1; rule.Values[0] := AValue1;
+ rule.ValueKinds[1] := AValueKind2; rule.Values[1] := AValue2;
+
+ rule.ShowValue := not AHideValue;
+ rule.Reverse := AReverse;
+
+ Result := AddRule(ASheet, ARange, rule);
+end;
+
+{ IconSet conditional format for 4 icons, i.e. 3 values }
+function TsConditionalFormatList.AddIconSetRule(ASheet: TsBasicWorksheet;
+ ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AValueKind3: TsCFValueKind; AValue3: Double;
+ AHideValue: Boolean = false; AReverse: Boolean = false): Integer;
+var
+ rule: TsCFIconSetRule;
+ n: Integer;
+begin
+ rule := TsCFIconSetRule.Create;
+ rule.IconSet := AIconset;
+ n := rule.IconCount;
+ if n <> 4 then begin
+ rule.Free;
+ Result := -1;
+ exit;
+ end;
+
+ rule.ValueKinds[0] := AValueKind1; rule.Values[0] := AValue1;
+ rule.ValueKinds[1] := AValueKind2; rule.Values[1] := AValue2;
+ rule.ValueKinds[2] := AValueKind3; rule.Values[2] := AValue3;
+
+ rule.ShowValue := not AHideValue;
+ rule.Reverse := AReverse;
+
+ Result := AddRule(ASheet, ARange, rule);
+end;
+
+{ Iconset conditional format for 5 icons, i.e. 4 values }
+function TsConditionalFormatList.AddIconSetRule(ASheet: TsBasicWorksheet;
+ ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double; AValueKind2: TsCFValueKind; AValue2: Double;
+ AValueKind3: TsCFValueKind; AValue3: Double; AValueKind4: TsCFValueKind; AValue4: Double;
+ AHideValue: Boolean = false; AReverse: Boolean = false): Integer;
+var
+ rule: TsCFIconSetRule;
+ n: Integer;
+begin
+ rule := TsCFIconSetRule.Create;
+ rule.IconSet := AIconset;
+ n := rule.IconCount;
+ if n <> 5 then begin
+ rule.Free;
+ Result := -1;
+ exit;
+ end;
+
+ rule.ValueKinds[0] := AValueKind1; rule.Values[0] := AValue1;
+ rule.ValueKinds[1] := AValueKind2; rule.Values[1] := AValue2;
+ rule.ValueKinds[2] := AValueKind3; rule.Values[2] := AValue3;
+ rule.ValueKinds[3] := AValueKind4; rule.Values[3] := AValue4;
+
+ rule.ShowValue := not AHideValue;
+ rule.Reverse := AReverse;
+
+ Result := AddRule(ASheet, ARange, rule);
+end;
+
+
{@@ ----------------------------------------------------------------------------
Deletes the conditional format at the given index from the list.
Iterates also through all cell in the range of the CF and removess the
diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas
index ec94b640c..9951a6361 100644
--- a/components/fpspreadsheet/source/common/fpsopendocument.pas
+++ b/components/fpspreadsheet/source/common/fpsopendocument.pas
@@ -118,6 +118,7 @@ type
procedure ReadCFCellFormat(ANode: TDOMNode; ASheet: TsBasicWorksheet; ARange: TsCellRange);
procedure ReadCFColorScale(ANode: TDOMNode; ASheet: TsBasicWorksheet; ARange: TsCellRange);
procedure ReadCFDataBars(ANode: TDOMNode; ASheet: TsBasicWorksheet; ARange: TsCellRange);
+ procedure ReadCFIconSet(ANode: TDOMNode; ASheet: TsBasicWorksheet; ARange: TsCellRange);
procedure ReadColumns(ATableNode: TDOMNode);
procedure ReadColumnStyle(AStyleNode: TDOMNode);
procedure ReadConditionalFormats(ANode: TDOMNode; AWorksheet: TsBasicWorksheet);
@@ -425,6 +426,19 @@ const
'number' // vkValue
);
+ CF_ICON_SET: array[TsCFIconSet] of string = (
+ '3Arrows', '3ArrowsGray', '3Flags', // is3Arrows, is3ArrowsGray, is3Flags
+ '3TrafficLights1', '3TrafficLights2', // is3TrafficLights1, is3TrafficLights2
+ '3Signs', '3Symbols', '3Symbols2', // is3Signs, is3Symbols, is3Symbols2
+ '3Smilies', '3Stars', '3Triangles', // is3Smilies, is3Stars, is3Triangles
+ '3ColorSmilies', // is3ColorSmilies,
+ '4Arrows', '4ArrowsGray', // is4Arrows, is4ArrowsGray
+ '4RedToBlack', '4Rating', // is4RedToBlack, is4Rating,
+ '4RedToBlack', // is4TrafficLights, // not in ODS
+ '5Arrows', '5ArrowsGray', // is5Arrows, is5ArrowsGray
+ '5Rating', '5Quarters', '5Boxes' // is5Rating, is5Quarters, is5Boxes
+ );
+
function CFOperandToStr(v: variant; AWorksheet: TsWorksheet): String;
var
r,c: Cardinal;
@@ -2205,6 +2219,7 @@ begin
'calcext:condition': ReadCFCellFormat(childNode, AWorksheet, range);
'calcext:color-scale': ReadCFColorScale(childNode, AWorksheet, range);
'calcext:data-bar': ReadCFDataBars(childNode, AWorksheet, range);
+ 'calcext:icon-set': ReadCFIconSet(childNode, AWorksheet, range);
end;
childNode := childNode.NextSibling;
end;
@@ -4072,6 +4087,74 @@ begin
);
end;
+procedure TsSpreadOpenDocReader.ReadCFIconSet(ANode: TDOMNode;
+ ASheet: TsBasicWorksheet; ARange: TsCellRange);
+{
+
+
+
+ }
+var
+ sheet: TsWorksheet;
+ nodeName: String;
+ s: String;
+ values: array of double = nil;
+ kinds: array of TsCFValueKind = nil;
+ iconSet, tmp: TsCFIconSet;
+ sIconSet: String;
+ found: Boolean;
+ n: Integer;
+begin
+ if ANode = nil then
+ exit;
+
+ sIconSet := GetAttrValue(ANode, 'calcext:icon-set-type');
+ if sIconSet = '' then
+ exit;
+
+ for tmp in TsCFIconSet do
+ if sIconSet = CF_ICON_SET[tmp] then begin
+ iconSet := tmp;
+ found := true;
+ break;
+ end;
+
+ if (not found) then
+ exit;
+
+ // Number of icons
+ n := GetCFIconCount(iconSet);
+ if (n < 3) or (n > 5) then // only 3, 4 or 5 icons allowed
+ exit;
+
+ ANode := ANode.FirstChild;
+ while ANode <> nil do
+ begin
+ nodeName := ANode.NodeName;
+ if nodeName = 'calcext:formatting-entry' then
+ begin
+ s := GetAttrValue(ANode, 'calcext:value');
+ SetLength(values, Length(values)+1);
+ if not TryStrToFloat(s, values[High(values)], FPointSeparatorSettings) then
+ values[High(values)] := 0;
+
+ s := GetAttrValue(ANode, 'calcext:type');
+ SetLength(kinds, Length(kinds)+1);
+ kinds[High(kinds)] := StrToValueKind(s);
+ end;
+ ANode := ANode.NextSibling;
+ end;
+
+ sheet := TsWorksheet(ASheet);
+ // Ignore the first value because it is always 0
+ case n of
+ 3: sheet.WriteIconSet(ARange, iconSet, kinds[1], values[1], kinds[2], values[2]);
+ 4: sheet.WriteIconSet(ARange, iconSet, kinds[1], values[1], kinds[2], values[2], kinds[3], values[3]);
+ 5: sheet.WriteIconSet(ARange, iconSet, kinds[1], values[1], kinds[2], values[2], kinds[3], values[3], kinds[4], values[4]);
+ end;
+end;
+
+
{ Reads the cells in the given table. Loops through all rows, and then finds all
cells of each row. }
procedure TsSpreadOpenDocReader.ReadRowsAndCells(ATableNode: TDOMNode);
@@ -6263,7 +6346,8 @@ var
cf_cellRule: TsCFCellRule;
cf_DataBarRule: TsCFDataBarRule;
cf_ColorRangeRule: TsCFColorRangeRule;
- i,j: Integer;
+ cf_IconSetRule: TsCFIconSetRule;
+ i, j, k: Integer;
sheet: TsWorksheet;
rangeStr: String;
firstCellStr: string;
@@ -6358,6 +6442,24 @@ begin
CF_VALUE_KIND[cf_ColorRangeRule.EndValueKind],
ColorToHTMLColorStr(cf_ColorRangeRule.EndColor)
]));
+ end else
+ if cf.Rules[j] is TsCFIconSetRule then
+ begin
+ cf_IconSetRule := TsCFIconSetRule(cf.Rules[j]);
+ AppendToStream(AStream, Format(
+ '', [
+ CF_ICON_SET[cf_IconSetRule.IconSet]
+ ]));
+ AppendToStream(AStream,
+ '');
+ for k := 0 to cf_IconSetRule.IconCount-2 do
+ AppendToStream(AStream, Format(
+ '', [
+ cf_IconSetRule.Values[k],
+ CF_VALUE_KIND[cf_IconSetRule.ValueKinds[k]]
+ ]));
+ AppendToStream(AStream,
+ '');
end;
end;
diff --git a/components/fpspreadsheet/source/common/fpspreadsheet.pas b/components/fpspreadsheet/source/common/fpspreadsheet.pas
index 89b00b5ff..b7f6fa9d4 100644
--- a/components/fpspreadsheet/source/common/fpspreadsheet.pas
+++ b/components/fpspreadsheet/source/common/fpspreadsheet.pas
@@ -405,6 +405,24 @@ type
function WriteDataBars(ARange: TsCellRange; ABarColor: TsColor;
AStartKind: TsCFValueKind; AStartValue: Double;
AEndKind: TsCFValueKind; AEndValue: Double): Integer; overload;
+ // icon sets
+ function WriteIconSet(ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AHideText: Boolean = false; AReverse: Boolean = false): Integer; overload;
+ function WriteIconSet(ARange: TsCellRange; AIconSet: TsCFIconSet; // 3 icons
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AHideText: Boolean = false; AReverse: Boolean = false): Integer; overload;
+ function WriteIconSet(ARange: TsCellRange; AIconSet: TsCFIconSet; // 4 icons
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AValueKind3: TsCFValueKind; AValue3: Double;
+ AHideText: Boolean = false; AReverse: Boolean = false): Integer; overload;
+ function WriteIconSet(ARange: TsCellRange; AIconSet: TsCFIconSet; // 5 icons
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AValueKind3: TsCFValueKind; AValue3: Double;
+ AValueKind4: TsCFValueKind; AValue4: Double;
+ AHideText: Boolean = false; AReverse: Boolean = false): Integer; overload;
{ Formulas }
function BuildRPNFormula(ACell: PCell; ADestCell: PCell = nil): TsRPNFormula;
diff --git a/components/fpspreadsheet/source/common/fpspreadsheet_cf.inc b/components/fpspreadsheet/source/common/fpspreadsheet_cf.inc
index cc644ff7d..bdb5526b7 100644
--- a/components/fpspreadsheet/source/common/fpspreadsheet_cf.inc
+++ b/components/fpspreadsheet/source/common/fpspreadsheet_cf.inc
@@ -143,6 +143,72 @@ begin
end;
+{@@ ----------------------------------------------------------------------------
+ Writes the conditional format "icon set"
+-------------------------------------------------------------------------------}
+
+function TsWorksheet.WriteIconSet(ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AHideText: Boolean = false; AReverse: Boolean = false): Integer;
+begin
+ Result := FWorkbook.FConditionalFormatList.AddIconSetRule(Self, ARange,
+ AIconSet,
+ AHideText, AReverse);
+ StoreCFIndexInCells(self, Result, ARange);
+end;
+
+function TsWorksheet.WriteIconSet(ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AHideText: Boolean = false; AReverse: Boolean = false): Integer;
+begin
+ Result := FWorkbook.FConditionalFormatList.AddIconSetRule(Self, ARange,
+ AIconSet,
+ AValueKind1, AValue1,
+ AValueKind2, AValue2,
+ AHideText, AReverse
+ );
+ if Result <> -1 then
+ StoreCFIndexInCells(self, Result, ARange);
+end;
+
+function TsWorksheet.WriteIconSet(ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AValueKind3: TsCFValueKind; AValue3: Double;
+ AHideText: Boolean = false; AReverse: Boolean = false): Integer;
+begin
+ Result := FWorkbook.FConditionalFormatList.AddIconSetRule(Self, ARange,
+ AIconSet,
+ AValueKind1, AValue1,
+ AValueKind2, AValue2,
+ AValueKind3, AValue3,
+ AHideText, AReverse
+ );
+ if Result <> -1 then
+ StoreCFIndexInCells(self, Result, ARange);
+end;
+
+function TsWorksheet.WriteIconSet(ARange: TsCellRange; AIconSet: TsCFIconSet;
+ AValueKind1: TsCFValueKind; AValue1: Double;
+ AValueKind2: TsCFValueKind; AValue2: Double;
+ AValueKind3: TsCFValueKind; AValue3: Double;
+ AValueKind4: TsCFValueKind; AValue4: Double;
+ AHideText: Boolean = false; AReverse: Boolean = false): Integer;
+begin
+ Result := FWorkbook.FConditionalFormatList.AddIconSetRule(Self, ARange,
+ AIconSet,
+ AValueKind1, AValue1,
+ AValueKind2, AValue2,
+ AValueKind3, AValue3,
+ AValueKind4, AValue4,
+ AHideText, AReverse
+ );
+ if Result <> -1 then
+ StoreCFIndexInCells(self, Result, ARange);
+end;
+
+
+
{==============================================================================}
{ TsWorkbook code for conditional formats }
{==============================================================================}
diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas
index d1dc7ad6f..1cf995585 100644
--- a/components/fpspreadsheet/source/common/xlsxooxml.pas
+++ b/components/fpspreadsheet/source/common/xlsxooxml.pas
@@ -82,6 +82,8 @@ type
ARange: TsCellRange);
procedure ReadCFExpression(ANode: TDOMNode; AWorksheet: TsBasicWorksheet;
ARange: TsCellRange; AFormatIndex: Integer);
+ procedure ReadCFIconSet(ANode: TDOMNode; AWorksheet: TsBasicWorksheet;
+ ARange: TsCellRange);
procedure ReadCFMisc(ANode: TDOMNode; AWorksheet: TsBasicWorksheet;
ARange: TsCellRange; AFormatIndex: Integer);
procedure ReadCFTop10(ANode: TDOMNode; AWorksheet: TsBasicWorksheet;
@@ -159,18 +161,14 @@ type
function PrepareFormula(const AFormula: String): String;
procedure ResetStreams;
procedure WriteBorderList(AStream: TStream);
+ procedure WriteCFCellRule(AStream: TStream; ARule: TsCFCellRule; ARange: TsCellRange; APriority: Integer);
+ procedure WriteCFColorRangeRule(AStream: TStream; ARule: TsCFColorRangeRule; APriority: Integer);
+ procedure WriteCFDataBarRule(AStream: TStream; ARule: TsCFDatabarRule; APriority: Integer);
+ procedure WriteCFIconSetRule(AStream: TStream; ARule: TsCFIconSetRule; APriority: Integer);
procedure WriteColBreaks(AStream: TStream; AWorksheet: TsBasicWorksheet);
procedure WriteCols(AStream: TStream; AWorksheet: TsBasicWorksheet);
procedure WriteComments(AWorksheet: TsBasicWorksheet);
procedure WriteConditionalFormat(AStream: TStream; AFormat: TsConditionalFormat; var APriority: Integer);
- procedure WriteConditionalFormatCellRule(AStream: TStream; ARule: TsCFCellRule;
- ARange: TsCellRange; APriority: Integer);
- procedure WriteConditionalFormatColorRangeRule(AStream: TStream; ARule: TsCFColorRangeRule;
- APriority: Integer);
- procedure WriteConditionalFormatDataBarRule(AStream: TStream; ARule: TsCFDatabarRule;
- APriority: Integer);
- procedure WriteConditionalFormatRule(AStream: TStream; ARule: TsCFRule;
- const ARange: TsCellRange; var APriority: Integer);
procedure WriteConditionalFormats(AStream: TStream; AWorksheet: TsBasicWorksheet);
procedure WriteDefinedNames(AStream: TStream);
procedure WriteDifferentialFormat(AStream: TStream; AFormat: PsCellFormat);
@@ -275,7 +273,7 @@ procedure InitOOXMLLimitations(out ALimitations: TsSpreadsheetFormatLimitations)
implementation
uses
- variants, strutils, math, lazutf8, LazFileUtils, uriparser,
+ variants, strutils, math, lazutf8, LazFileUtils, uriparser, typinfo,
{%H-}fpsPatches, fpSpreadsheet, fpsCrypto, fpsExprParser,
fpsStrings, fpsStreams, fpsClasses, fpsImages;
@@ -446,6 +444,20 @@ const
'' // cfcExpression
);
+ CF_ICON_SET: array[TsCFIconSet] of string = (
+ '3Arrows', '3ArrowsGray','3Flags', // is3Arrows, is3ArrowsGray, is3Flags
+ '3TrafficLights2', // REPLACEMENT FOR is3TrafficLights1 which requires x14
+ '3TrafficLights', // is3TrafficLights2
+ '3Signs', '3Symbols', '3Symbols2', // is3Signs, is3Symbols, is3Symbols2
+ '3Signs', '3Signs', '3Signs', '3Signs', // REPLACEMENT FOR is3Smilies, is3Stars, is3Triangles, is3ColorSmilies which need x14
+ '4Arrows', '4ArrowsGray', // is4Arrows, is4ArrowsGray
+ '4RedToBlack', '4Rating', // is4RedToBlack, is4Rating
+ '4TrafficLights', // is4TrafficLights,
+ '5Arrows', '5ArrowsGray', // is5Arrows, is5ArrowsGray,
+ '5Rating', '5Quarters', // is5Rating, is5Quarters,
+ '5Quarters' // REPLACEMENT FOR is5Boxes which needs x14
+ );
+
function StrToFillStyle(s: String): TsFillStyle;
var
fs: TsFillStyle;
@@ -1533,6 +1545,79 @@ begin
end;
end;
+procedure TsSpreadOOXMLReader.ReadCFIconSet(ANode: TDOMNode;
+ AWorksheet: TsBasicWorksheet; ARange: TsCellRange);
+var
+ sheet: TsWorksheet;
+ s: String;
+ sIconSet: String;
+ iconSet: TsCFIconSet;
+ found: Boolean;
+ tmpIconSet: TsCFIconSet;
+ nodeName: String;
+ iv: Integer;
+ vk: array of TsCFValueKind = nil;
+ v: array of Double = nil;
+ n: Integer;
+ x: Double;
+ res: Integer;
+begin
+ ANode := ANode.FirstChild;
+ if (ANode <> nil) and (ANode.NodeName = 'iconSet') then
+ begin
+ sIconSet := GetAttrValue(ANode, 'iconSet');
+ found := false;
+ for tmpIconSet in TsCFIconSet do
+ if 'is' + sIconSet = GetEnumName(typeInfo(TsCFIconSet), integer(tmpIconSet)) then
+ begin
+ iconSet := tmpIconSet;
+ found := true;
+ break;
+ end;
+
+ if (not found) or (sIconSet = '') then
+ Exit;
+
+ // Determine icon count from name of icon set
+ n := GetCFIconCount(iconSet);
+ if (n < 3) or (n > 5) then // only 3, 4 or 5 icons allowed
+ Exit;
+
+ SetLength(v, n);
+ SetLength(vk, n);
+ iv := 0;
+ ANode := ANode.FirstChild;
+ while (ANode <> nil) do
+ begin
+ nodeName := ANode.NodeName;
+ if (nodeName = 'cfvo') and (iv <= High(vk)) then
+ begin
+ s := GetAttrValue(ANode, 'type');
+ vk[iv] := StrToCFValueKind(s);
+ s := GetAttrValue(ANode, 'val');
+ if TryStrToFloat(s, x, FPointSeparatorSettings) then
+ v[iv] := x
+ else
+ v[iv] := 0.0;
+ inc(iv);
+ if iv >= n then
+ break;
+ end;
+ ANode := ANode.NextSibling;
+ end;
+
+ sheet := TsWorksheet(AWorksheet);
+ // Ignore the first value because it is always 0
+ case n of
+ 3: res := sheet.WriteIconSet(ARange, iconSet, vk[1], v[1], vk[2], v[2]);
+ 4: res := sheet.WriteIconSet(ARange, iconSet, vk[1], v[1], vk[2], v[2], vk[3], v[3]);
+ 5: res := sheet.WriteIconSet(ARange, iconSet, vk[1], v[1], vk[2], v[2], vk[3], v[3], vk[4], v[4]);
+ end;
+
+ ANode := ANode.NextSibling;
+ end;
+end;
+
procedure TsSpreadOOXMLReader.ReadCFMisc(ANode: TDOMNode;
AWorksheet: TsBasicWorksheet; ARange: TsCellRange; AFormatIndex: Integer);
var
@@ -1887,6 +1972,8 @@ begin
ReadCFColorRange(childNode, AWorksheet, range);
'dataBar':
ReadCFDataBars(childNode, AWorksheet, range);
+ 'iconSet':
+ ReadCFIconSet(childNode, AWorksheet, range);
end;
end;
childNode := childNode.NextSibling;
@@ -3913,6 +4000,203 @@ begin
'');
end;
+
+procedure TsSpreadOOXMLWriter.WriteCFCellRule(AStream: TStream;
+ ARule: TsCFCellRule; ARange: TsCellRange; APriority: Integer);
+const
+ FORMULA: array[cfcBeginsWith..cfcNotContainsErrors] of String = (
+ 'LEFT(%0:s,LEN("%1:s"))="%1:s"', // cfcBeginsWith
+ 'RIGHT(%0:s,Len("%1:s"))="%1:s"', // cfcEndsWidth
+ 'NOT(ISERROR(SEARCH("%1:s",%0:s)))', // cfcContainsText
+ 'ISERROR(SEARCH("%1:s",%0:s))', // cfcNotContainsText
+ 'ISERROR(%0:s)', // cfcContainsErrors
+ 'NOT(ISERROR(%0:s))' // cfcNotContainsErrors
+ );
+var
+ i: Integer;
+ dxfID: Integer;
+ typeStr, opStr, formula1Str, formula2Str, param1Str, param2Str, param3Str: String;
+ firstCellOfRange: String;
+ s: String;
+begin
+ dxfID := -1;
+ for i := 0 to High(FDifferentialFormatIndexList) do
+ if FDifferentialFormatIndexList[i] = ARule.FormatIndex then
+ begin
+ dxfID := i;
+ break;
+ end;
+
+ typeStr := CF_TYPE_NAMES[ARule.Condition];
+ if CF_OPERATOR_NAMES[ARule.Condition] = '' then
+ opStr := ''
+ else
+ opStr := ' operator="' + CF_OPERATOR_NAMES[ARule.Condition] + '"';
+ formula1Str := '';
+ formula2Str := '';
+ param1Str := '';
+ param2Str := '';
+ param3Str := '';
+ case ARule.Condition of
+ cfcEqual..cfcNotBetween:
+ begin
+ s := CFOperandToStr(ARule.Operand1);
+ formula1Str := Format('%s', [s]);
+ if (ARule.Condition in [cfcBetween, cfcNotBetween]) then
+ begin
+ s := CFOperandToStr(ARule.Operand2);
+ formula2Str := Format('%s', [s]);
+ end;
+ end;
+ cfcAboveAverage..cfcBelowEqualAverage:
+ begin
+ if (ARule.Condition in [cfcBelowAverage, cfcBelowEqualAverage]) then
+ param1Str := ' aboveAverage="0"';
+ if (ARule.Condition in [cfcAboveEqualAverage, cfcBelowEqualAverage]) then
+ param2Str := ' equalAverage="1"';
+ if VarIsNumeric(ARule.Operand1) or (ARule.Operand1 = 0) then
+ param3Str := Format(' stdDev="%g"', [double(ARule.Operand1)]);
+ end;
+ cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent:
+ begin
+ // // = bottom 30 percent
+ if ARule.Condition in [cfcBottom, cfcBottomPercent] then
+ param1Str := ' bottom="1"';
+ if ARule.Condition in [cfcTopPercent, cfcBottomPercent] then
+ param2Str := ' percent="1"';
+ param3Str := ' rank="' + VarToStr(ARule.Operand1) + '"';
+ end;
+ cfcDuplicate, cfcUnique:
+ ;
+ cfcBeginsWith..cfcNotContainsErrors:
+ begin
+ firstCellOfRange := GetCellString(ARange.Row1, ARange.Col1);
+ formula1Str :=
+ '' +
+ Format(FORMULA[ARule.Condition], [firstcellOfRange, ARule.Operand1]) +
+ '';
+ param1Str := ' text="' + VarToStr(ARule.Operand1) + '"';
+ end;
+ cfcExpression:
+ begin
+ s := ARule.Operand1;
+ if (s <> '') and (s[1] = '=') then Delete(s, 1, 1);
+ formula1Str := '' + s + '';
+ end;
+ else
+ FWorkbook.AddErrorMsg('ConditionalFormat operator not supported.');
+ end;
+
+ if formula1Str = '' then
+ s := Format(
+ '', [
+ typeStr, dxfId, APriority, opStr, param1Str, param2Str, param3Str
+ ])
+ else
+ s := Format(
+ '' +
+ '%s%s' +
+ '', [
+ typeStr, dxfId, APriority, opStr, param1Str, param2Str, param3Str,
+ formula1Str, formula2Str
+ ]);
+ AppendToStream(AStream, s);
+end;
+
+
+procedure TsSpreadOOXMLWriter.WriteCFColorRangeRule(AStream: TStream;
+ ARule: TsCFColorRangeRule; APriority: Integer);
+{ example:
+
+
+
+
+
+
+
+
+
+ }
+begin
+ AppendToStream(AStream,
+ '' +
+ '');
+ AppendToStream(AStream,
+ CF_ValueNode(ARule.StartValueKind, ARule.StartValue),
+ IfThen(ARule.ThreeColors, CF_ValueNode(ARule.CenterValueKind, ARule.CenterValue), ''),
+ CF_ValueNode(ARule.EndValueKind, ARule.EndValue)
+ );
+ AppendToStream(AStream,
+ CF_ColorNode(ARule.StartColor),
+ IfThen(ARule.ThreeColors, CF_ColorNode(ARule.CenterColor), ''),
+ CF_ColorNode(ARule.EndColor)
+ );
+ AppendToStream(AStream,
+ '' +
+ '');
+end;
+
+
+procedure TsSpreadOOXMLWriter.WriteCFDatabarRule(AStream: TStream;
+ ARule: TsCFDataBarRule; APriority: Integer);
+{ example from test file:
+
+
+
+
+
+
+
+
+ {A620EE03-2FEC-4D54-872C-66BDB99CB07E}
+
+
+ }
+begin
+ AppendToStream(AStream,
+ '' +
+ '');
+
+ AppendToStream(AStream,
+ CF_ValueNode(ARule.StartValueKind, ARule.StartValue),
+ CF_ValueNode(ARule.EndValueKind, ARule.EndValue),
+ CF_ColorNode(ARule.Color) );
+
+ AppendToStream(AStream,
+ '' +
+ '');
+end;
+
+
+procedure TsSpreadOOXMLWriter.WriteCFIconSetRule(AStream: TStream;
+ ARule: TsCFIconSetRule; APriority: Integer);
+{
+
+
+
+
+
+ }
+var
+ i: Integer;
+begin
+ AppendToStream(AStream, Format(
+ '' +
+ '' +
+ '', [
+ APriority, CF_ICON_SET[Arule.IconSet]
+ ]));
+
+ for i := 0 to ARule.IconCount-2 do
+ AppendToStream(AStream,
+ CF_ValueNode(ARule.ValueKinds[i], ARule.Values[i])
+ );
+ AppendToStream(AStream,
+ '' +
+ '');
+end;
+
+
procedure TsSpreadOOXMLWriter.WriteColBreaks(AStream: TStream;
AWorksheet: TsBasicWorksheet);
var
@@ -4045,192 +4329,25 @@ begin
for i := 0 to AFormat.RulesCount-1 do
begin
rule := AFormat.Rules[i];
- WriteConditionalFormatRule(AStream, rule, AFormat.CellRange, APriority);
+ if rule is TsCFCellRule then
+ WriteCFCellRule(AStream, TsCFCellRule(rule), AFormat.CellRange, APriority)
+ else
+ if rule is TsCFColorRangeRule then
+ WriteCFColorRangeRule(AStream, TsCFColorRangeRule(rule), APriority)
+ else
+ if rule is TsCFDataBarRule then
+ WriteCFDataBarRule(AStream, TsCFDataBarRule(rule), APriority)
+ else
+ if rule is TsCFIconSetRule then
+ WriteCFIconSetRule(AStream, TsCFIconSetRule(rule), APriority)
+ else
+ exit;
+ dec(APriority);
end;
AppendToStream(AStream,
'');
end;
-procedure TsSpreadOOXMLWriter.WriteConditionalFormatCellRule(AStream: TStream;
- ARule: TsCFCellRule; ARange: TsCellRange; APriority: Integer);
-const
- FORMULA: array[cfcBeginsWith..cfcNotContainsErrors] of String = (
- 'LEFT(%0:s,LEN("%1:s"))="%1:s"', // cfcBeginsWith
- 'RIGHT(%0:s,Len("%1:s"))="%1:s"', // cfcEndsWidth
- 'NOT(ISERROR(SEARCH("%1:s",%0:s)))', // cfcContainsText
- 'ISERROR(SEARCH("%1:s",%0:s))', // cfcNotContainsText
- 'ISERROR(%0:s)', // cfcContainsErrors
- 'NOT(ISERROR(%0:s))' // cfcNotContainsErrors
- );
-var
- i: Integer;
- dxfID: Integer;
- typeStr, opStr, formula1Str, formula2Str, param1Str, param2Str, param3Str: String;
- firstCellOfRange: String;
- s: String;
-begin
- dxfID := -1;
- for i := 0 to High(FDifferentialFormatIndexList) do
- if FDifferentialFormatIndexList[i] = ARule.FormatIndex then
- begin
- dxfID := i;
- break;
- end;
-
- typeStr := CF_TYPE_NAMES[ARule.Condition];
- if CF_OPERATOR_NAMES[ARule.Condition] = '' then
- opStr := ''
- else
- opStr := ' operator="' + CF_OPERATOR_NAMES[ARule.Condition] + '"';
- formula1Str := '';
- formula2Str := '';
- param1Str := '';
- param2Str := '';
- param3Str := '';
- case ARule.Condition of
- cfcEqual..cfcNotBetween:
- begin
- s := CFOperandToStr(ARule.Operand1);
- formula1Str := Format('%s', [s]);
- if (ARule.Condition in [cfcBetween, cfcNotBetween]) then
- begin
- s := CFOperandToStr(ARule.Operand2);
- formula2Str := Format('%s', [s]);
- end;
- end;
- cfcAboveAverage..cfcBelowEqualAverage:
- begin
- if (ARule.Condition in [cfcBelowAverage, cfcBelowEqualAverage]) then
- param1Str := ' aboveAverage="0"';
- if (ARule.Condition in [cfcAboveEqualAverage, cfcBelowEqualAverage]) then
- param2Str := ' equalAverage="1"';
- if VarIsNumeric(ARule.Operand1) or (ARule.Operand1 = 0) then
- param3Str := Format(' stdDev="%g"', [double(ARule.Operand1)]);
- end;
- cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent:
- begin
- // // = bottom 30 percent
- if ARule.Condition in [cfcBottom, cfcBottomPercent] then
- param1Str := ' bottom="1"';
- if ARule.Condition in [cfcTopPercent, cfcBottomPercent] then
- param2Str := ' percent="1"';
- param3Str := ' rank="' + VarToStr(ARule.Operand1) + '"';
- end;
- cfcDuplicate, cfcUnique:
- ;
- cfcBeginsWith..cfcNotContainsErrors:
- begin
- firstCellOfRange := GetCellString(ARange.Row1, ARange.Col1);
- formula1Str :=
- '' +
- Format(FORMULA[ARule.Condition], [firstcellOfRange, ARule.Operand1]) +
- '';
- param1Str := ' text="' + VarToStr(ARule.Operand1) + '"';
- end;
- cfcExpression:
- begin
- s := ARule.Operand1;
- if (s <> '') and (s[1] = '=') then Delete(s, 1, 1);
- formula1Str := '' + s + '';
- end;
- else
- FWorkbook.AddErrorMsg('ConditionalFormat operator not supported.');
- end;
-
- if formula1Str = '' then
- s := Format(
- '', [
- typeStr, dxfId, APriority, opStr, param1Str, param2Str, param3Str
- ])
- else
- s := Format(
- '' +
- '%s%s' +
- '', [
- typeStr, dxfId, APriority, opStr, param1Str, param2Str, param3Str,
- formula1Str, formula2Str
- ]);
- AppendToStream(AStream, s);
-end;
-
-procedure TsSpreadOOXMLWriter.WriteConditionalFormatColorRangeRule(AStream: TStream;
- ARule: TsCFColorRangeRule; APriority: Integer);
-{ example:
-
-
-
-
-
-
-
-
-
- }
-begin
- AppendToStream(AStream,
- '' +
- '');
- AppendToStream(AStream,
- CF_ValueNode(ARule.StartValueKind, ARule.StartValue),
- IfThen(ARule.ThreeColors, CF_ValueNode(ARule.CenterValueKind, ARule.CenterValue), ''),
- CF_ValueNode(ARule.EndValueKind, ARule.EndValue)
- );
- AppendToStream(AStream,
- CF_ColorNode(ARule.StartColor),
- IfThen(ARule.ThreeColors, CF_ColorNode(ARule.CenterColor), ''),
- CF_ColorNode(ARule.EndColor)
- );
- AppendToStream(AStream,
- '' +
- '');
-end;
-
-procedure TsSpreadOOXMLWriter.WriteConditionalFormatDatabarRule(AStream: TStream;
- ARule: TsCFDataBarRule; APriority: Integer);
-{ example from test file:
-
-
-
-
-
-
-
-
- {A620EE03-2FEC-4D54-872C-66BDB99CB07E}
-
-
- }
-begin
- AppendToStream(AStream,
- '' +
- '');
-
- AppendToStream(AStream,
- CF_ValueNode(ARule.StartValueKind, ARule.StartValue),
- CF_ValueNode(ARule.EndValueKind, ARule.EndValue),
- CF_ColorNode(ARule.Color) );
-
- AppendToStream(AStream,
- '' +
- '');
-end;
-
-procedure TsSpreadOOXMLWriter.WriteConditionalFormatRule(AStream: TStream;
- ARule: TsCFRule; const ARange: TsCellRange; var APriority: Integer);
-begin
- if ARule is TsCFCellRule then
- WriteConditionalFormatCellRule(AStream, TsCFCellRule(ARule), ARange, APriority)
- else
- if ARule is TsCFColorRangeRule then
- WriteConditionalFormatColorRangeRule(AStream, TsCFColorRangeRule(ARule), APriority)
- else
- if ARule is TsCFDataBarRule then
- WriteConditionalFormatDataBarRule(AStream, TsCFDataBarRule(ARule), APriority)
- else
- exit;
- dec(APriority);
-end;
-
procedure TsSpreadOOXMLWriter.WriteConditionalFormats(AStream: TStream;
AWorksheet: TsBasicWorksheet);
var
diff --git a/components/fpspreadsheet/tests/conditionalformattests.pas b/components/fpspreadsheet/tests/conditionalformattests.pas
index 7a7532cae..b673212b4 100644
--- a/components/fpspreadsheet/tests/conditionalformattests.pas
+++ b/components/fpspreadsheet/tests/conditionalformattests.pas
@@ -34,12 +34,18 @@ type
procedure TestWriteRead_CF_CellFmt(AFileFormat: TsSpreadsheetFormat;
ACondition: TsCFCondition; ACellFormat: TsCellFormat);
+ // Test color range format
procedure TestWriteRead_CF_ColorRange(AFileFormat: TsSpreadsheetFormat;
ThreeColors: Boolean; FullSyntax: Boolean);
+ // Test data bars format
procedure TestWriteRead_CF_DataBars(AFileFormat: TsSpreadsheetFormat;
FullSyntax: Boolean);
+ // Test icon set format
+ procedure TestWriteRead_CF_IconSet(AFileFormat: TsSpreadsheetFormat;
+ AIconSet: TsCFIconSet; FullSyntax: Boolean);
+
published
{ Excel XLSX }
procedure TestWriteRead_CF_CellFmt_XLSX_Equal_Const;
@@ -81,6 +87,10 @@ type
procedure TestWriteRead_CF_Databars_XLSX_Full;
procedure TestWriteRead_CF_Databars_XLSX_Simple;
+ procedure TestWriteRead_CF_Iconset_XLSX_Full_5Quarters;
+ procedure TestWriteRead_CF_IconSet_XLSX_Simple_3Arrows;
+ procedure TestWriteRead_CF_IconSet_XLSX_Simple_5Rating;
+
{ Excel XML }
procedure TestWriteRead_CF_CellFmt_XML_Equal_Const;
procedure TestWriteRead_CF_CellFmt_XML_NotEqual_Const;
@@ -151,6 +161,9 @@ type
procedure TestWriteRead_CF_Databars_ODS_Full;
procedure TestWriteRead_CF_Databars_ODS_Simple;
+ procedure TestWriteRead_CF_Iconset_ODS_Full_5Quarters;
+ procedure TestWriteRead_CF_IconSet_ODS_Simple_3Arrows;
+ procedure TestWriteRead_CF_IconSet_ODS_Simple_5Rating;
end;
implementation
@@ -1541,6 +1554,188 @@ begin
end;
+
+{-------------------------------------------------------------------------------
+ IconSet tests
+-------------------------------------------------------------------------------}
+procedure TSpreadWriteReadCFTests.TestWriteRead_CF_IconSet(
+ AFileFormat: TsSpreadsheetFormat; AIconSet: TsCFIconSet; FullSyntax: Boolean);
+const
+ SHEET_NAME = 'CF';
+ SOLL_VALUE_KIND_1 = vkValue;
+ SOLL_VALUE_KIND_2 = vkPercent;
+ SOLL_VALUE_KIND_3 = vkPercentile;
+ SOLL_VALUE_KIND_4 = vkPercent;
+ SOLL_VALUE_1 = 15;
+ SOLL_VALUE_2 = 42;
+ SOLL_VALUE_3 = 62;
+ SOLL_VALUE_4 = 85;
+
+var
+ worksheet: TsWorksheet;
+ workbook: TsWorkbook;
+ row, col: Cardinal;
+ tempFile: string;
+ cf: TsConditionalFormat;
+ rule: TsCFIconSetRule;
+ sollRange: TsCellRange;
+ actRange: TsCellRange;
+ actIconSet: TsCFIconSet;
+ n: Integer;
+begin
+ // Write out all test values
+ workbook := TsWorkbook.Create;
+ try
+ workbook.Options := [boAutoCalc];
+ workSheet:= workBook.AddWorksheet(SHEET_NAME);
+
+ // Add test cells (numeric)
+ row := 0;
+ for Col := 0 to 9 do
+ worksheet.WriteNumber(row, col, col*10.0);
+
+ // Write conditional formats
+ sollRange := Range(0, 0, 0, 9);
+ if FullSyntax then
+ begin
+ n := GetCFIconCount(AIconSet);
+ case n of
+ 3: worksheet.WriteIconSet(sollRange, AIconSet, SOLL_VALUE_KIND_1, SOLL_VALUE_1, SOLL_VALUE_KIND_2, SOLL_VALUE_2);
+ 4: worksheet.WriteIconSet(sollRange, AIconSet, SOLL_VALUE_KIND_1, SOLL_VALUE_1, SOLL_VALUE_KIND_2, SOLL_VALUE_2, SOLL_VALUE_KIND_3, SOLL_VALUE_3);
+ 5: worksheet.WriteIconSet(sollRange, AIconSet, SOLL_VALUE_KIND_1, SOLL_VALUE_1, SOLL_VALUE_KIND_2, SOLL_VALUE_2, SOLL_VALUE_KIND_3, SOLL_VALUE_3, SOLL_VALUE_KIND_4, SOLL_VALUE_4);
+ end;
+ end else
+ worksheet.WriteIconSet(sollRange, AIconSet);
+
+ // Save to file
+ tempFile := NewTempFile;
+ workBook.WriteToFile(tempFile, AFileFormat, true);
+ finally
+ workbook.Free;
+ end;
+
+ // Open the spreadsheet
+ workbook := TsWorkbook.Create;
+ try
+ workbook.ReadFromFile(TempFile, AFileFormat);
+ worksheet := GetWorksheetByName(workBook, SHEET_NAME);
+
+ if worksheet=nil then
+ fail('Error in test code. Failed to get named worksheet');
+
+ // Check count of conditional formats
+ CheckEquals(1, workbook.GetNumConditionalFormats, 'ConditionalFormat count mismatch.');
+
+ // Read conditional format
+ cf := Workbook.GetConditionalFormat(0);
+
+ //Check range
+ actRange := cf.CellRange;
+ CheckEquals(sollRange.Row1, actRange.Row1, 'Conditional format range mismatch (Row1)');
+ checkEquals(sollRange.Col1, actRange.Col1, 'Conditional format range mismatch (Col1)');
+ CheckEquals(sollRange.Row2, actRange.Row2, 'Conditional format range mismatch (Row2)');
+ checkEquals(sollRange.Col2, actRange.Col2, 'Conditional format range mismatch (Col2)');
+
+ // Check rules count
+ CheckEquals(1, cf.RulesCount, 'Conditional format rules count mismatch');
+
+ // Check rules class
+ CheckEquals(TsCFIconSetRule, cf.Rules[0].ClassType, 'Conditional format rule class mismatch');
+
+ // Now know that the rule is a TsCFIconsetRule
+ rule := TsCFIconSetRule(cf.Rules[0]);
+
+ // Check icon set
+ actIconSet := rule.IconSet;
+ CheckEquals(
+ GetEnumName(TypeInfo(TsCFIconSet), Integer(AIconSet)),
+ GetEnumName(TypeInfo(TsCFIconSet), Integer(actIconSet)),
+ 'IconSet format: icon set mismatch');
+
+ // Parameters
+ if FullSyntax then
+ begin
+ CheckEquals(
+ GetEnumName(TypeInfo(TsCFValueKind), integer(SOLL_VALUE_KIND_1)),
+ GetEnumName(TypeInfo(TsCFValueKind), integer(rule.ValueKinds[0])),
+ 'IconSet format: value kind 0 mismatch.'
+ );
+ if not (SOLL_VALUE_KIND_1 in [vkMin, vkMax]) then
+ CheckEquals(SOLL_VALUE_1, rule.Values[0], 'IconSet format: value 0 mismatch');
+
+ CheckEquals(
+ GetEnumName(TypeInfo(TsCFValueKind), integer(SOLL_VALUE_KIND_2)),
+ GetEnumName(TypeInfo(TsCFValueKind), integer(rule.ValueKinds[1])),
+ 'IconSet format: value kind 1 mismatch.'
+ );
+ if not (SOLL_VALUE_KIND_2 in [vkMin, vkMax]) then
+ CheckEquals(SOLL_VALUE_2, rule.Values[1], 'IconSet format: value 1 mismatch');
+
+ if n > 2 then
+ begin
+ CheckEquals(
+ GetEnumName(TypeInfo(TsCFValueKind), integer(SOLL_VALUE_KIND_3)),
+ GetEnumName(TypeInfo(TsCFValueKind), integer(rule.ValueKinds[2])),
+ 'IconSet format: value kind 2 mismatch.'
+ );
+ if not (SOLL_VALUE_KIND_3 in [vkMin, vkMax]) then
+ CheckEquals(SOLL_VALUE_3, rule.Values[2], 'IconSet format: value 2 mismatch');
+ end;
+
+ if n = 3 then
+ begin
+ CheckEquals(
+ GetEnumName(TypeInfo(TsCFValueKind), integer(SOLL_VALUE_KIND_4)),
+ GetEnumName(TypeInfo(TsCFValueKind), integer(rule.ValueKinds[3])),
+ 'IconSet format: value kind 3 mismatch.'
+ );
+ if not (SOLL_VALUE_KIND_4 in [vkMin, vkMax]) then
+ CheckEquals(SOLL_VALUE_4, rule.Values[3], 'IconSet format: value 3 mismatch');
+ end;
+ end;
+
+ finally
+ workbook.Free;
+ DeleteFile(tempFile);
+ end;
+end;
+
+
+{ Excel XLSX }
+
+procedure TSpreadWriteReadCFTests.TestWriteRead_CF_Iconset_XLSX_Full_5Quarters;
+begin
+ TestWriteRead_CF_IconSet(sfOOXML, is5Quarters, true);
+end;
+
+procedure TSpreadWriteReadCFTests.TestWriteRead_CF_IconSet_XLSX_Simple_3Arrows;
+begin
+ TestWriteRead_CF_IconSet(sfOOXML, is3Arrows, false);
+end;
+
+procedure TSpreadWriteReadCFTests.TestWriteRead_CF_IconSet_XLSX_Simple_5Rating;
+begin
+ TestWriteRead_CF_IconSet(sfOOXML, is5Rating, false);
+end;
+
+{ OpenDocument }
+
+procedure TSpreadWriteReadCFTests.TestWriteRead_CF_Iconset_ODS_Full_5Quarters;
+begin
+ TestWriteRead_CF_IconSet(sfOpenDocument, is5Quarters, true);
+end;
+
+procedure TSpreadWriteReadCFTests.TestWriteRead_CF_IconSet_ODS_Simple_3Arrows;
+begin
+ TestWriteRead_CF_IconSet(sfOpenDocument, is3Arrows, false);
+end;
+
+procedure TSpreadWriteReadCFTests.TestWriteRead_CF_IconSet_ODS_Simple_5Rating;
+begin
+ TestWriteRead_CF_IconSet(sfOpenDocument, is5Rating, false);
+end;
+
+
initialization
RegisterTest(TSpreadWriteReadCFTests);
diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi
index c1922ef74..7dc947e60 100644
--- a/components/fpspreadsheet/tests/spreadtestgui.lpi
+++ b/components/fpspreadsheet/tests/spreadtestgui.lpi
@@ -1,13 +1,11 @@
-
+
-
-
-
+