From 9bc1afa59ba7901d7dd7c3959276106362e5b78b Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Sun, 5 Jul 2020 22:45:12 +0000 Subject: [PATCH] fpspreadsheet: Add reading of conditional formats for xlsx. Not complete, and still fragile. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7526 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- components/fpspreadsheet/README.txt | 5 +- .../source/common/fpsconditionalformat.pas | 6 +- .../source/common/fpsreaderwriter.pas | 2 + .../fpspreadsheet/source/common/xlsxooxml.pas | 530 +++++++++++++++--- 4 files changed, 454 insertions(+), 89 deletions(-) diff --git a/components/fpspreadsheet/README.txt b/components/fpspreadsheet/README.txt index e705f4921..9468d5771 100644 --- a/components/fpspreadsheet/README.txt +++ b/components/fpspreadsheet/README.txt @@ -11,8 +11,9 @@ spreadsheet documents in various formats: - Comma-separated text files - html files - wikimedia wikitable formats -- there is also a demo file showing how to install user-defined readers/writers - for other formats (.slk). +- the user can register his own readers/writers for other formats. An example + is shown in the "spready" application for the .slk format. + ("spready" is in the applications folder of CCR). The library is written in a very flexible manner, capable of being extended to support any number of formats easily. diff --git a/components/fpspreadsheet/source/common/fpsconditionalformat.pas b/components/fpspreadsheet/source/common/fpsconditionalformat.pas index c28ee5361..c9c085d0b 100644 --- a/components/fpspreadsheet/source/common/fpsconditionalformat.pas +++ b/components/fpspreadsheet/source/common/fpsconditionalformat.pas @@ -305,8 +305,8 @@ var begin rule := TsCFCellRule.Create; rule.Condition := ACondition; - rule.Operand1 := varNull; - rule.Operand2 := varNull; + VarClear(rule.Operand1); + VarClear(rule.Operand2); rule.FormatIndex := ACellFormatIndex; Result := AddRule(ASheet, ARange, rule); end; @@ -320,7 +320,7 @@ begin rule := TsCFCellRule.Create; rule.Condition := ACondition; rule.Operand1 := AParam; - rule.Operand2 := varNull; + VarClear(rule.Operand2); rule.FormatIndex := ACellFormatIndex; Result := AddRule(ASheet, ARange, rule); end; diff --git a/components/fpspreadsheet/source/common/fpsreaderwriter.pas b/components/fpspreadsheet/source/common/fpsreaderwriter.pas index 318c08568..7e68ad05d 100644 --- a/components/fpspreadsheet/source/common/fpsreaderwriter.pas +++ b/components/fpspreadsheet/source/common/fpsreaderwriter.pas @@ -83,6 +83,8 @@ type protected {@@ list of format records collected from the file } FCellFormatList: TsCellFormatList; + {@@ list for differential formats needed by conditional formatting} + FDifferentialFormatList: TFPList; {@@ List of fonts collected from the file } FFontList: TFPList; {@@ Temporary cell for virtual mode} diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas index 664094d71..2191c158d 100644 --- a/components/fpspreadsheet/source/common/xlsxooxml.pas +++ b/components/fpspreadsheet/source/common/xlsxooxml.pas @@ -59,6 +59,7 @@ type FBorderList: TFPList; FHyperlinkList: TFPList; FSharedFormulaBaseList: TFPList; + FDxfList: TFPList; FPalette: TsPalette; FThemeColors: array of TsColor; FLastRow, FLastCol: Cardinal; @@ -68,15 +69,20 @@ type function FindCommentsFileName(ANode: TDOMNode): String; procedure ReadActiveSheet(ANode: TDOMNode; out ActiveSheetIndex: Integer); procedure ReadBorders(ANode: TDOMNode); + function ReadBorderStyle(ANode: TDOMNode; out ABorderStyle: TsCellBorderStyle): Boolean; procedure ReadCell(ANode: TDOMNode; AWorksheet: TsBasicWorksheet; ARowIndex: Cardinal; var AColIndex: Cardinal); procedure ReadCellXfs(ANode: TDOMNode); + procedure ReadCFRule(ANode: TDOMNode; AWorksheet: TsBasicWorksheet; ARange: TsCellRange); procedure ReadColRowBreaks(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); function ReadColor(ANode: TDOMNode): TsColor; procedure ReadCols(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); procedure ReadComments(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); + procedure ReadConditionalFormatting(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); procedure ReadDateMode(ANode: TDOMNode); procedure ReadDefinedNames(ANode: TDOMNode); + procedure ReadDifferentialFormat(ANode: TDOMNode); + procedure ReadDifferentialFormats(ANode: TDOMNode); procedure ReadDimension(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); procedure ReadFileVersion(ANode: TDOMNode); procedure ReadFills(ANode: TDOMNode); @@ -330,6 +336,16 @@ type end; TDifferentialFormatData = class + CellFormatIndex: Integer; + UsedFormattingFields: TsUsedFormattingFields; + Borders: TsCellBorders; + BorderStyles: TsCellBorderStyles; + FillPatternType: TsFillStyle; + FillFgColor: TsColor; + FillBgColor: TsColor; + FontColor: TsColor; + FontStyles: TsFontStyles; + NumFormatStr: String; end; THyperlinkListData = class @@ -394,6 +410,66 @@ const 'slantDashDot' // lsSlantDashDot ); + CF_TYPE_NAMES: array[TsCFCondition] of String = ( + 'cellIs', 'cellIs', // cfcEqual, cfcNotEqual, + 'cellIs', 'cellIs', 'cellIs', 'cellIs', // cfcGreaterThan, cfcLessThan, cfcGreaterEqual, cfcLessEqual, + 'cellIs', 'cellIs', // cfcBetween, cfcNotBetween, + 'aboveAverage', 'aboveAverage', 'aboveAverage', 'aboveAverage', // cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage, + 'top10', 'top10', 'top10', 'top10', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, + 'duplicateValues', 'uniqueValues', // cfcDuplicate, cfcUnique, + 'beginsWith', 'endsWith', // cfcBeginsWith, cfcEndsWith, + 'containsText', 'notContainsText', // cfcContainsText, cfcNotContainsText, + 'containsErrors', 'notContainsErrors' // cfcContainsErrors, cfcNotContainsErrors + ); + + CF_OPERATOR_NAMES: array[TsCFCondition] of string = ( + 'equal', 'notEqual', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual', + 'between', 'notBetween', + '', '', '', '', // cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage, + '', '', '', '', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, + '', '', // cfcDuplicate, cfcUnique, + '', '', '', 'notContains', //cfcBeginsWith, cfcEndsWith, cfcContainsText, cfcNotContainsText, + '', '' // cfcContainsErrors, cfcNotContainsErrors + ); + +function StrToFillStyle(s: String): TsFillStyle; +var + fs: TsFillStyle; +begin + if s = '' then + begin + Result := fsSolidFill; + exit; + end; + + for fs in TsFillStyle do + if PATTERN_TYPES[fs] = s then + begin + Result := fs; + exit; + end; + Result := fsNoFill; +end; + +function StrToLineStyle(s: String): TsLineStyle; +var + ls: TsLineStyle; +begin + if s = '' then + begin + Result := lsThin; + exit; + end; + + for ls in TsLineStyle do + if LINESTYLE_TYPES[ls] = s then + begin + Result := ls; + exit; + end; + Result := lsThin; +end; + function CFOperandToStr(v: Variant): String; const ERR = cardinal(-1); @@ -468,6 +544,7 @@ begin FBorderList := TFPList.Create; FHyperlinkList := TFPList.Create; FCellFormatList := TsCellFormatList.Create(true); + FDifferentialFormatList := TFPList.Create; // Allow duplicates because xf indexes used in cell records cannot be found any more. FSharedFormulaBaseList := TFPList.Create; @@ -495,6 +572,10 @@ begin TObject(FHyperlinkList[j]).Free; FHyperlinkList.Free; + for j := FDifferentialFormatList.Count-1 downto 0 do + TObject(FDifferentialFormatList[j]).Free; + FDifferentialFormatList.Free; + for j := FSheetList.Count-1 downto 0 do TObject(FSheetList[j]).Free; FSheetList.Free; @@ -613,59 +694,6 @@ begin end; procedure TsSpreadOOXMLReader.ReadBorders(ANode: TDOMNode); - - function ReadBorderStyle(ANode: TDOMNode; - out ABorderStyle: TsCellBorderStyle): Boolean; - var - s: String; - colorNode: TDOMNode; - nodeName: String; - begin - Result := false; - ABorderStyle.LineStyle := lsThin; - ABorderStyle.Color := scBlack; - - s := GetAttrValue(ANode, 'style'); - if (s = '') or (s = 'none') then - exit; - - if s = 'thin' then - ABorderStyle.LineStyle := lsThin - else if s = 'medium'then - ABorderStyle.LineStyle := lsMedium - else if s = 'thick' then - ABorderStyle.LineStyle := lsThick - else if s = 'dotted' then - ABorderStyle.LineStyle := lsDotted - else if s = 'dashed' then - ABorderStyle.LineStyle := lsDashed - else if s = 'double' then - ABorderStyle.LineStyle := lsDouble - else if s = 'hair' then - ABorderStyle.LineStyle := lsHair - else if s = 'dashDot' then - ABorderStyle.LineStyle := lsDashDot - else if s = 'dashDotDot' then - ABorderStyle.LineStyle := lsDashDotDot - else if s = 'mediumDashed' then - ABorderStyle.LineStyle := lsMediumDash - else if s = 'mediumDashDot' then - ABorderStyle.LineSTyle := lsMediumDashDot - else if s = 'mediumDashDotDot' then - ABorderStyle.LineStyle := lsMediumDashDotDot - else if s = 'slantDashDot' then - ABorderStyle.LineStyle := lsSlantDashDot; - - colorNode := ANode.FirstChild; - while Assigned(colorNode) do begin - nodeName := colorNode.NodeName; - if (nodeName = 'color') or (nodename = 'x:color') then - ABorderStyle.Color := ReadColor(colorNode); - colorNode := colorNode.NextSibling; - end; - Result := true; - end; - var borderNode: TDOMNode; edgeNode: TDOMNode; @@ -727,6 +755,61 @@ begin end; end; +function TsSpreadOOXMLReader.ReadBorderStyle(ANode: TDOMNode; + out ABorderStyle: TsCellBorderStyle): Boolean; +var + s: String; + colorNode: TDOMNode; + nodeName: String; +begin + Result := false; + ABorderStyle.LineStyle := lsThin; + ABorderStyle.Color := scBlack; + + s := GetAttrValue(ANode, 'style'); + if (s = '') or (s = 'none') then + exit; + + ABorderStyle.LineStyle := StrToLineStyle(s); + { + if s = 'thin' then + ABorderStyle.LineStyle := lsThin + else if s = 'medium'then + ABorderStyle.LineStyle := lsMedium + else if s = 'thick' then + ABorderStyle.LineStyle := lsThick + else if s = 'dotted' then + ABorderStyle.LineStyle := lsDotted + else if s = 'dashed' then + ABorderStyle.LineStyle := lsDashed + else if s = 'double' then + ABorderStyle.LineStyle := lsDouble + else if s = 'hair' then + ABorderStyle.LineStyle := lsHair + else if s = 'dashDot' then + ABorderStyle.LineStyle := lsDashDot + else if s = 'dashDotDot' then + ABorderStyle.LineStyle := lsDashDotDot + else if s = 'mediumDashed' then + ABorderStyle.LineStyle := lsMediumDash + else if s = 'mediumDashDot' then + ABorderStyle.LineSTyle := lsMediumDashDot + else if s = 'mediumDashDotDot' then + ABorderStyle.LineStyle := lsMediumDashDotDot + else if s = 'slantDashDot' then + ABorderStyle.LineStyle := lsSlantDashDot; + } + + colorNode := ANode.FirstChild; + while Assigned(colorNode) do begin + nodeName := colorNode.NodeName; + if (nodeName = 'color') or (nodename = 'x:color') then + ABorderStyle.Color := ReadColor(colorNode); + colorNode := colorNode.NextSibling; + end; + Result := true; +end; + procedure TsSpreadOOXMLReader.ReadCell(ANode: TDOMNode; AWorksheet: TsBasicWorksheet; ARowIndex: Cardinal; var AColIndex: Cardinal); var @@ -1152,6 +1235,101 @@ begin end; end; +procedure TsSpreadOOXMLReader.ReadCFRule(ANode: TDOMNode; AWorksheet: TsBasicWorksheet; + ARange: TsCellRange); +var + nodeName: String; + s, sType, sOp: String; + sFormula: Array of String; + cf: TsCFCondition; + found: Boolean; + i: Integer; + n: Integer; + x: Double; + r, c: Cardinal; + dxf: TDifferentialFormatData; + dxfId: Integer; + condition: TsCFCondition; + values: array of Variant; + fmtIdx: Integer; + sheet: TsWorksheet; +begin + sheet := TsWorksheet(AWorksheet); + + found := false; + + sType := GetAttrValue(ANode, 'type'); + sOp := GetAttrValue(ANode, 'operator'); + + if sType = 'cellIs' then + begin + for cf in TsCFCondition do + if sOp = CF_OPERATOR_NAMES[cf] then + begin + found := true; + condition := cf; + break; + end + end + else + for cf in TsCFCondition do + if sType = CF_TYPE_NAMES[cf] then + begin + found := true; + condition := cf; + break; + end; + if not found then + exit; + + s := GetAttrValue(ANode, 'dxfId'); + if not TryStrToInt(s, dxfId) then + exit; + dxf := TDifferentialFormatData(FDifferentialFormatList[dxfId]); + fmtIdx := dxf.CellFormatIndex; + + SetLength(sFormula, 0); + ANode := ANode.FirstChild; + while (ANode <> nil) do + begin + nodeName := ANode.NodeName; + if nodeName = 'formula' then + begin + SetLength(sFormula, Length(sFormula) + 1); + sFormula[High(sFormula)] := GetNodeValue(ANode); + end; + ANode := ANode.NextSibling; + end; + + if condition in [cfcEqual..cfcNotBetween] then begin + SetLength(values, Length(sFormula)); + for i := 0 to High(sFormula) do begin + values[i] := sFormula[i]; + if (sFormula[i] <> '') then begin + if TryStrToInt(sFormula[i], n) then + values[i] := n + else if TryStrToFloat(sFormula[i], x, FPointSeparatorSettings) then + values[i] := x + else if sFormula[i][1] = '"' then + values[i] := sFormula[i] + else if ParseCellString(sFormula[i], r, c) then + values[i] := sFormula[i] + else + values[i] := '=' + sFormula[i]; + end; + end; + end else + exit; + + case Length(values) of + 0: sheet.WriteConditionalCellFormat(ARange, condition, fmtIdx); + 1: sheet.WriteConditionalCellFormat(ARange, condition, values[0], fmtIdx); + 2: sheet.WriteConditionalCellFormat(ARange, condition, values[0], values[1], fmtIdx); + end; +end; + + + procedure TsSpreadOOXMLReader.ReadColRowBreaks(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); var @@ -1391,6 +1569,36 @@ begin end; end; +procedure TsSpreadOOXMLReader.ReadConditionalFormatting(ANode: TDOMNode; + AWorksheet: TsBasicWorksheet); +var + childNode: TDOMNode; + nodeName: string; + range: TsCellRange; + s: String; +begin + while ANode <> nil do + begin + nodeName := ANode.NodeName; + if (nodeName = 'conditionalFormatting') then + begin + s := GetAttrValue(ANode, 'sqref'); + if ParseCellRangeString(s, range) then + begin + childNode := ANode.FirstChild; + while childNode <> nil do + begin + nodeName := childNode.NodeName; + if nodeName = 'cfRule' then + ReadCFRule(childNode, AWorksheet, range); + childNode := childNode.NextSibling; + end; + end; + end; + ANode := ANode.NextSibling; + end; +end; + procedure TsSpreadOOXMLReader.ReadDateMode(ANode: TDOMNode); var s: String; @@ -1503,6 +1711,186 @@ begin end; end; +procedure TsSpreadOOXMLReader.ReadDifferentialFormat(ANode: TDOMNode); +var + nodeName: String; + childNode: TDOMNode; + pattNode: TDOMNode; + s: String; + fontStyles: TsFontStyles; + fontColor: TsColor; + bgColor: TsColor; + fgColor: TsColor; + fillPatt: String; + borders: TsCellBorders; + borderStyles: TsCellBorderStyles; + uff: TsUsedFormattingFields; + dxf: TDifferentialFormatData; + fmt: TsCellFormat; + numFmtStr: String; +begin + uff := []; + borders := []; + fontStyles := []; + fontColor := scNotDefined; + numFmtStr := ''; + + ANode := ANode.FirstChild; + while ANode <> nil do + begin + nodeName := ANode.NodeName; + if nodeName = 'font' then + begin + // read font + uff := uff + [uffFont]; + childNode := ANode.FirstChild; + while childNode <> nil do + begin + nodeName := childNode.NodeName; + if (nodename = 'b') then + fontStyles := fontStyles + [fssBold] + else + if (nodename = 'i') then + fontStyles := fontStyles + [fssItalic] + else + if (nodeName = 'strike') then + fontStyles := fontStyles + [fssStrikeOut] + else + if (nodename = 'color') then + fontColor := ReadColor(childNode); + childNode := childNode.NextSibling; + end; + end else + if nodeName = 'border' then + begin + // read border + uff := uff + [uffBorder]; + childNode := ANode.FirstChild; + while childNode <> nil do + begin + nodeName := childNode.NodeName; + if nodeName = 'left' then + begin + borders := borders + [cbWest]; + ReadBorderStyle(childNode, borderStyles[cbWest]); + end else + if nodeName = 'top' then + begin + borders := borders + [cbNorth]; + ReadBorderStyle(childNode, borderStyles[cbNorth]); + end else + if nodeName = 'right' then + begin + borders := borders + [cbEast]; + ReadBorderStyle(childNode, borderStyles[cbEast]); + end else + if nodeName = 'bottom' then + begin + borders := borders + [cbSouth]; + ReadBorderStyle(childNode, borderStyles[cbSouth]); + end; + childNode := childNode.NextSibling; + end; + end else + if nodeName = 'fill' then + begin + // read fill + uff := uff + [uffBackground]; + pattNode := ANode.FirstChild; + while pattNode <> nil do begin + nodeName := pattNode.NodeName; + if nodeName = 'patternFill' then + begin + fillPatt := GetAttrValue(pattNode, 'patternType'); + childNode := pattNode.FirstChild; + while childNode <> nil do + begin + nodeName := childNode.NodeName; + if nodeName = 'bgColor' then + bgColor := ReadColor(childNode) + else if nodeName = 'fgColor' then + fgColor := ReadColor(childNode); + childNode := childNode.NextSibling; + end; + end; + pattNode := pattNode.NextSibling; + end; + { + fillPatt := GetAttrValue(ANode, 'patternType'); + childNode := ANode.FirstChild; + while childNode <> nil do begin + nodeName := childNode.NodeName; + if nodeName = 'bgColor' then + bgColor := ReadColor(childNode) + else + if nodeName ='fgColor' then + fgColor := ReadColor(childNode); + childNode := childNode.NextSibling; + end; + } + end else + if nodeName = 'numFmt' then + begin + // Nuzmber format + uff := uff + [uffNumberFormat]; + numFmtStr := GetAttrValue(ANode, 'formatCode'); + end; + ANode := ANode.NextSibling; + end; + + InitFormatRecord(fmt); + if uff <> [] then + begin + dxf := TDifferentialFormatData.Create; + dxf.UsedFormattingFields := uff; + fmt.UsedFormattingFields := uff; + if (uffBackground in uff) then + begin + dxf.FillPatternType := StrToFillStyle(fillPatt); + dxf.FillBgColor := bgColor; + dxf.FillFgColor := fgColor; + fmt.SetBackground(dxf.FillPatternType, dxf.FillFgColor, dxf.FillBgColor); + end; + if (uffBorder in uff) then + begin + dxf.Borders := borders; + dxf.BorderStyles := borderStyles; + fmt.Border := borders; + fmt.BorderStyles := borderStyles; + end; + if (uffFont in uff) then + begin + dxf.FontColor := fontColor; + dxf.FontStyles := fontStyles; + fmt.FontIndex := TsWorkbook(FWorkbook).AddFont('', -1, fontStyles, fontColor); + end else + if (uffNumberFormat in uff) then + begin + dxf.NumFormatStr := numFmtStr; + fmt.NumberFormatStr := numFmtStr; + fmt.NumberFormatIndex := TsWorkbook(FWorkbook).AddNumberFormat(numFmtStr); + end; + dxf.CellFormatIndex := TsWorkbook(FWorkbook).AddCellFormat(fmt); + FDifferentialFormatList.Add(dxf); + end; +end; + +procedure TsSpreadOOXMLReader.ReadDifferentialFormats(ANode: TDOMNode); +var + nodeName: String; +begin + if ANode = nil then + exit; + ANode := ANode.FirstChild; + while ANode <> nil do + begin + nodeName := ANode.NodeName; + if nodeName = 'dxf' then + ReadDifferentialFormat(ANode); + ANode := ANode.NextSibling; + end; +end; + procedure TsSpreadOOXMLReader.ReadDimension(ANode: TDOMNode; AWorksheet: TsBasicWorksheet); var @@ -2790,6 +3178,7 @@ begin ReadBorders(Doc_FindNode('borders')); ReadNumFormats(Doc_FindNode('numFmts')); ReadCellXfs(Doc_FindNode('cellXfs')); + ReadDifferentialFormats(DOC_FindNode('dxfs')); FreeAndNil(Doc); end; finally @@ -2844,6 +3233,7 @@ begin ReadSheetFormatPr(Doc_FindNode('sheetFormatPr'), FWorksheet); ReadCols(Doc_FindNode('cols'), FWorksheet); ReadWorksheet(Doc_FindNode('sheetData'), FWorksheet); + ReadConditionalFormatting(Doc_FindNode('conditionalFormatting'), FWorksheet); ReadSheetProtection(Doc_FindNode('sheetProtection'), FWorksheet); ReadMergedCells(Doc_FindNode('mergeCells'), FWorksheet); ReadHyperlinks(Doc_FindNode('hyperlinks'), FWorksheet); @@ -3382,34 +3772,6 @@ end; procedure TsSpreadOOXMLWriter.WriteConditionalFormatCellRule(AStream: TStream; ARule: TsCFCellRule; ARange: TsCellRange; APriority: Integer); const - TYPE_NAMES: array[TsCFCondition] of String = ( - 'cellIs', 'cellIs', // cfcEqual, cfcNotEqual, - 'cellIs', 'cellIs', 'cellIs', 'cellIs', // cfcGreaterThan, cfcLessThan, cfcGreaterEqual, cfcLessEqual, - 'cellIs', 'cellIs', // cfcBetween, cfcNotBetween, - 'aboveAverage', 'aboveAverage', 'aboveAverage', 'aboveAverage', // cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage, - 'top10', 'top10', 'top10', 'top10', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, - 'duplicateValues', 'uniqueValues', // cfcDuplicate, cfcUnique, - 'beginsWith', 'endsWith', // cfcBeginsWith, cfcEndsWith, - 'containsText', 'notContainsText', // cfcContainsText, cfcNotContainsText, - 'containsErrors', 'notContainsErrors' // cfcContainsErrors, cfcNotContainsErrors - ); - OPERATOR_NAMES: array[TsCFCondition] of string = ( - 'equal', 'notEqual', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual', - 'between', 'notBetween', - '', '', '', '', // cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage, - '', '', '', '', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, - '', '', // cfcDuplicate, cfcUnique, - '', '', '', 'notContains', //cfcBeginsWith, cfcEndsWith, cfcContainsText, cfcNotContainsText, - '', '' // cfcContainsErrors, cfcNotContainsErrors - ); -{ - OPERATOR_NAMES_1: array[cfcEqual..cfcLessEqual] of String = - ('equal', 'notEqual', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual'); - OPERATOR_NAMES_2: array[cfcBetween..cfcNotBetween] of String = - ('between', 'notBetween'); - TYPE_NAMES: array[cfcBeginsWith..cfcNotContainsErrors] of String = - ('beginsWith', 'endsWith', 'containsText', 'notContainsText', 'containsErrors', 'notContainsErrors'); - } FORMULA: array[cfcBeginsWith..cfcNotContainsErrors] of String = ( 'LEFT(%0:s,LEN("%1:s"))="%1:s"', // cfcBeginsWith 'RIGHT(%0:s,Len("%1:s"))="%1:s"', // cfcEndsWidth @@ -3433,11 +3795,11 @@ begin break; end; - typeStr := TYPE_NAMES[ARule.Condition]; - if OPERATOR_NAMES[ARule.Condition] = '' then + typeStr := CF_TYPE_NAMES[ARule.Condition]; + if CF_OPERATOR_NAMES[ARule.Condition] = '' then opStr := '' else - opStr := ' operator="' + OPERATOR_NAMES[ARule.Condition] + '"'; + opStr := ' operator="' + CF_OPERATOR_NAMES[ARule.Condition] + '"'; formula1Str := ''; formula2Str := ''; param1Str := '';