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 b07aa5ad7..c30f9f37d 100644 --- a/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas +++ b/components/fpspreadsheet/examples/other/conditional_formatting/demo_conditional_formatting.pas @@ -265,6 +265,15 @@ begin fmtIdx := wb.AddCellFormat(fmt); sh.WriteConditionalCellFormat(Range(row, 2, row, lastCol), cfcNotContainsErrors, fmtIdx); + // conditional format: expression + inc(row); + sh.WriteText(row, 0, 'expression: ISNUMBER($E$5)'); + sh.WriteText(row, 1, 'background blue'); + InitFormatRecord(fmt); + fmt.SetBackgroundColor(scBlue); + fmtIdx := wb.AddCellFormat(fmt); + sh.WriteConditionalCellFormat(Range(row, 2, row, 2), cfcExpression, '=ISNUMBER($E$5)', fmtIdx); + // Two rules in the same conditional format inc(row); sh.WriteText(row, 0, 'Two rules: #1: equal to 5, #2: equal to 3'); diff --git a/components/fpspreadsheet/source/common/fpsconditionalformat.pas b/components/fpspreadsheet/source/common/fpsconditionalformat.pas index f32299041..c05a07d5c 100644 --- a/components/fpspreadsheet/source/common/fpsconditionalformat.pas +++ b/components/fpspreadsheet/source/common/fpsconditionalformat.pas @@ -23,7 +23,8 @@ type cfcDuplicate, cfcUnique, cfcBeginsWith, cfcEndsWith, cfcContainsText, cfcNotContainsText, - cfcContainsErrors, cfcNotContainsErrors + cfcContainsErrors, cfcNotContainsErrors, + cfcExpression ); TsCFCellRule = class(TsCFRule) diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas index 9ca29f2e3..f037c3af4 100644 --- a/components/fpspreadsheet/source/common/fpsopendocument.pas +++ b/components/fpspreadsheet/source/common/fpsopendocument.pas @@ -388,26 +388,28 @@ const 'cell-content()>%s', 'cell-content()<%s', //cfcGreaterThan, cfcLessThan 'cell-content()>=%s', 'cell-content<=%s', // cfcGreaterEqual, cfdLessEqual 'cell-is-between(%s,%s)', 'cell-is-not-between(%s,%s)', // cfcBetween, cfcNotBetween, - '', '', '', '', // cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage - '', '', '', '', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, - '', '', // cfcDuplicate, cfcUnique, - '', '', '', '', // cfcBeginsWith, cfcEndsWith, cfcContainsText, cfcNotContainsText, - '', '' // cfcContainsErrors, cfcNotContainsErrors + '', '', '', '', // cfcAboveAverage, cfcBelowAverage, cfcAboveEqualAverage, cfcBelowEqualAverage + '', '', '', '', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, + '', '', // cfcDuplicate, cfcUnique, + '', '', '', '', // cfcBeginsWith, cfcEndsWith, cfcContainsText, cfcNotContainsText, + '', '', // cfcContainsErrors, cfcNotContainsErrors + 'is-true-formula(%s)' // cfcExpression ); CF_CALCEXT_OP: array[TsCFCondition] of string = ( - '=%s', '!=%s', // cfcEqual, cfcNotEqual - '>%s', '<%s', //cfcGreaterThan, cfcLessThan - '>=%s', '<=%s', // cfcGreaterEqual, cfdLessEqual - 'between(%s,%s)', 'not-between(%s,%s)', // cfcBetween, cfcNotBetween, - 'above-average', 'below-average', // cfcAboveAverage, cfcBelowAverage, + '=%s', '!=%s', // cfcEqual, cfcNotEqual + '>%s', '<%s', //cfcGreaterThan, cfcLessThan + '>=%s', '<=%s', // cfcGreaterEqual, cfdLessEqual + 'between(%s,%s)', 'not-between(%s,%s)', // cfcBetween, cfcNotBetween, + 'above-average', 'below-average', // cfcAboveAverage, cfcBelowAverage, 'above-equal-average', 'below-equal-average', // cfcAboveEqualAverage, cfcBelowEqualAverage 'top-elements(%s)', 'bottom-elements(%s)', // cfcTop, cfcBottom, 'top-percent(%s)', 'bottom-percent(%s)', // cfcTopPercent, cfcBottomPercent, 'duplicate', 'unique', // cfcDuplicate, cfcUnique, 'begins-with(%s)', 'ends-with(%s)', // cfcBeginsWith, cfcEndsWith, 'contains-text(%s)', 'not-contains-text(%s)', // cfcContainsText, cfcNotContainsText, - 'is-error', 'is-no-error' // cfcContainsErrors, cfcNotContainsErrors + 'is-error', 'is-no-error', // cfcContainsErrors, cfcNotContainsErrors + 'formula-is(%s)' // cfcExprssion ); CF_VALUE_KIND: array[TsCFValueKind] of string = ( @@ -7398,8 +7400,18 @@ begin begin cf_cellRule := TsCFCellRule(cf.Rules[k]); cf_styleName := Format('conditional_%d', [cf_cellRule.FormatIndex]); - operand1Str := CFOperandToStr(cf_cellrule.Operand1, cf_sheet); - operand2Str := CFOperandToStr(cf_cellrule.Operand2, cf_sheet); + if cf_cellRule.Condition = cfcExpression then + begin + operand1Str := VarToStr(cf_cellRule.Operand1); + if (operand1Str <> '') and (operand1Str[1] <> '=') then + operand1Str := '=' + operand1Str; + operand1Str := CFOperandToStr(operand1Str, cf_sheet); + operand2Str := ''; + end else + begin + operand1Str := CFOperandToStr(cf_cellrule.Operand1, cf_sheet); + operand2Str := CFOperandToStr(cf_cellrule.Operand2, cf_sheet); + end; cf_condition := Format(CF_STYLE_OP[cf_cellRule.Condition], [operand1Str, operand2Str]); if cf_Condition <> '' then begin diff --git a/components/fpspreadsheet/source/common/xlsxml.pas b/components/fpspreadsheet/source/common/xlsxml.pas index 1853deaf6..069d28d48 100644 --- a/components/fpspreadsheet/source/common/xlsxml.pas +++ b/components/fpspreadsheet/source/common/xlsxml.pas @@ -229,7 +229,8 @@ const '@NOT(ISERROR(SEARCH(%0:s,RC)))', // cfcContainsText '@ISERROR(SEARCH(%0:s,RC))', // cfcNotContainsText, '@ISERROR(RC)', // cfcContainsErrors - '@NOT(ISERROR(RC))' // cfcNotContainsErrors + '@NOT(ISERROR(RC))', // cfcNotContainsErrors + '@' // cfcExpression ); // The leading '@' indicates that the formula will be used in node @@ -2134,14 +2135,26 @@ begin Continue; end; - value1Str := CFOperandToStr(cfRule.Operand1, sheet); - value2Str := CFOperandToStr(cfRule.Operand2, sheet); + if cfRule.Condition = cfcExpression then + begin + s := cfRule.Operand1; + if (s <> '') and (s[1] <> '=') then s := '=' + s; + value1Str := CFOperandToStr(s, sheet); + value2Str := ''; + end else + begin + value1Str := CFOperandToStr(cfRule.Operand1, sheet); + value2Str := CFOperandToStr(cfRule.Operand2, sheet); + end; s := CF_CONDITIONS[cfRule.Condition]; if s[1] = '@' then begin Delete(s, 1,1); - s := Format(s, [value1Str, value2Str, rangeStr]); + if s = '' then + s := value1Str + else + s := Format(s, [value1Str, value2Str, rangeStr]); value1Str := s; s := ''; end; @@ -2152,9 +2165,11 @@ begin if s <> '' then AppendToStream(AStream, LF + INDENT4 + '' + s + ''); + if value1Str <> '' then AppendToStream(AStream, LF + INDENT4 + '' + value1Str + ''); + if (cfRule.Condition in [cfcBetween, cfcNotBetween]) and (value2Str <> '') then AppendToStream(AStream, LF + INDENT4 + '' + value2Str + ''); diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas index df321e665..2e32c8e40 100644 --- a/components/fpspreadsheet/source/common/xlsxooxml.pas +++ b/components/fpspreadsheet/source/common/xlsxooxml.pas @@ -430,7 +430,8 @@ const 'duplicateValues', 'uniqueValues', // cfcDuplicate, cfcUnique, 'beginsWith', 'endsWith', // cfcBeginsWith, cfcEndsWith, 'containsText', 'notContainsText', // cfcContainsText, cfcNotContainsText, - 'containsErrors', 'notContainsErrors' // cfcContainsErrors, cfcNotContainsErrors + 'containsErrors', 'notContainsErrors', // cfcContainsErrors, cfcNotContainsErrors + 'expression' // cfcExpression ); CF_OPERATOR_NAMES: array[TsCFCondition] of string = ( @@ -440,7 +441,8 @@ const '', '', '', '', // cfcTop, cfcBottom, cfcTopPercent, cfcBottomPercent, '', '', // cfcDuplicate, cfcUnique, '', '', '', 'notContains', //cfcBeginsWith, cfcEndsWith, cfcContainsText, cfcNotContainsText, - '', '' // cfcContainsErrors, cfcNotContainsErrors + '', '', // cfcContainsErrors, cfcNotContainsErrors + '' // cfcExpression ); function StrToFillStyle(s: String): TsFillStyle; @@ -4115,6 +4117,12 @@ begin ''; 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;