From b80085c78dd971e1218e606e15f886788dafae6f Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Tue, 30 Jun 2020 10:45:14 +0000 Subject: [PATCH] fpspreadsheet: Move conditional format list from worksheet to workbook (to facilitate handling for ODS). git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7503 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../source/common/fpsconditionalformat.pas | 83 +++++++++++-------- .../source/common/fpspreadsheet.pas | 27 ++++-- .../source/common/fpspreadsheet_cf.inc | 23 +---- .../fpspreadsheet/source/common/fpsutils.pas | 20 +++++ .../fpspreadsheet/source/common/xlsxooxml.pas | 61 +++++++------- 5 files changed, 123 insertions(+), 91 deletions(-) diff --git a/components/fpspreadsheet/source/common/fpsconditionalformat.pas b/components/fpspreadsheet/source/common/fpsconditionalformat.pas index 039b8f865..bfcc99d89 100644 --- a/components/fpspreadsheet/source/common/fpsconditionalformat.pas +++ b/components/fpspreadsheet/source/common/fpsconditionalformat.pas @@ -70,35 +70,36 @@ type { Conditional format item } TsConditionalFormat = class private + FWorksheet: TsBasicWorksheet; FCellRange: TsCellRange; FRules: TsCFRules; function GetRules(AIndex: Integer): TsCFRule; function GetRulesCount: Integer; public - constructor Create(ACellRange: TsCellRange); + constructor Create(AWorksheet: TsBasicWorksheet; ACellRange: TsCellRange); destructor Destroy; override; property CellRange: TsCellRange read FCellRange; property Rules[AIndex: Integer]: TsCFRule read GetRules; property RulesCount: Integer read GetRulesCount; + property Worksheet: TsBasicWorksheet read FWorksheet; end; TsConditionalFormatList = class(TFPObjectList) - private - FWorksheet: TsBasicWorksheet; protected - function AddRule(ARange: TsCellRange; ARule: TsCFRule): Integer; + function AddRule(ASheet: TsBasicWorksheet; ARange: TsCellRange; + ARule: TsCFRule): Integer; public - function AddCellRule(ARange: TsCellRange; ACondition: TsCFCondition; - ACellFormatIndex: Integer): Integer; overload; - function AddCellRule(ARange: TsCellRange; ACondition: TsCFCondition; - AParam: Variant; ACellFormatIndex: Integer): Integer; overload; - function AddCellRule(ARange: TsCellRange; ACondition: TsCFCondition; - AParam1, AParam2: Variant; ACellFormatIndex: Integer): Integer; overload; - procedure AddColorRangeRule(ARange: TsCellRange); - procedure AddDataBarRule(ARange: TsCellRange); + function AddCellRule(ASheet: TsBasicWorksheet; ARange: TsCellRange; + ACondition: TsCFCondition; ACellFormatIndex: Integer): Integer; overload; + function AddCellRule(ASheet: TsBasicWorksheet; ARange: TsCellRange; + ACondition: TsCFCondition; AParam: Variant; ACellFormatIndex: Integer): Integer; overload; + function AddCellRule(ASheet: TsBasicWorksheet; ARange: TsCellRange; + ACondition: TsCFCondition; AParam1, AParam2: Variant; ACellFormatIndex: Integer): Integer; overload; + procedure AddColorRangeRule(ASheet: TsBasicWorksheet; ARange: TsCellRange); + procedure AddDataBarRule(ASheet: TsBasicWorksheet; ARange: TsCellRange); procedure Delete(AIndex: Integer); - function Find(ARange: TsCellRange): Integer; + function Find(ASheet: TsBasicWorksheet; ARange: TsCellRange): Integer; end; @@ -170,9 +171,11 @@ end; { TsConditonalFormat } -constructor TsConditionalFormat.Create(ACellRange: TsCellRange); +constructor TsConditionalFormat.Create(AWorksheet: TsBasicWorksheet; + ACellRange: TsCellRange); begin inherited Create; + FWorksheet := AWorksheet; FCellRange := ACellRange; FRules := TsCFRules.Create; end; @@ -202,15 +205,15 @@ end; the rule describing the format. The rules are grouped for the same cell ranges. -------------------------------------------------------------------------------} -function TsConditionalFormatList.AddRule(ARange: TsCellRange; - ARule: TsCFRule): Integer; +function TsConditionalFormatList.AddRule(ASheet: TsBasicWorksheet; + ARange: TsCellRange; ARule: TsCFRule): Integer; var CF: TsConditionalFormat; idx: Integer; begin - idx := Find(ARange); + idx := Find(ASheet, ARange); if idx = -1 then begin - CF := TsConditionalFormat.Create(ARange); + CF := TsConditionalFormat.Create(ASheet, ARange); idx := Add(CF); end else CF := TsConditionalFormat(Items[idx]); @@ -220,8 +223,9 @@ end; // TODO: Add pre-checks for compatibility of condition and operands -function TsConditionalFormatList.AddCellRule(ARange: TsCellRange; - ACondition: TsCFCondition; ACellFormatIndex: Integer): Integer; +function TsConditionalFormatList.AddCellRule(ASheet: TsBasicWorksheet; + ARange: TsCellRange; ACondition: TsCFCondition; + ACellFormatIndex: Integer): Integer; var rule: TsCFCellRule; begin @@ -230,11 +234,12 @@ begin rule.Operand1 := varNull; rule.Operand2 := varNull; rule.FormatIndex := ACellFormatIndex; - Result := AddRule(ARange, rule); + Result := AddRule(ASheet, ARange, rule); end; -function TsConditionalFormatList.AddCellRule(ARange: TsCellRange; - ACondition: TsCFCondition; AParam: Variant; ACellFormatIndex: Integer): Integer; +function TsConditionalFormatList.AddCellRule(ASheet: TsBasicWorksheet; + ARange: TsCellRange; ACondition: TsCFCondition; AParam: Variant; + ACellFormatIndex: Integer): Integer; var rule: TsCFCellRule; begin @@ -243,11 +248,11 @@ begin rule.Operand1 := AParam; rule.Operand2 := varNull; rule.FormatIndex := ACellFormatIndex; - Result := AddRule(ARange, rule); + Result := AddRule(ASheet, ARange, rule); end; -function TsConditionalFormatList.AddCellRule(ARange: TsCellRange; - ACondition: TsCFCondition; AParam1, AParam2: Variant; +function TsConditionalFormatList.AddCellRule(ASheet: TsBasicWorksheet; + ARange: TsCellRange; ACondition: TsCFCondition; AParam1, AParam2: Variant; ACellFormatIndex: Integer): Integer; var rule: TsCFCellRule; @@ -257,15 +262,17 @@ begin rule.Operand1 := AParam1; rule.Operand2 := AParam2; rule.FormatIndex := ACellFormatIndex; - Result := AddRule(ARange, rule); + Result := AddRule(ASheet, ARange, rule); end; -procedure TsConditionalFormatList.AddColorRangeRule(ARange: TsCellRange); +procedure TsConditionalFormatList.AddColorRangeRule(ASheet: TsBasicWorksheet; + ARange: TsCellRange); begin raise EXception.Create('ColorRange not yet implemented.'); end; -procedure TsConditionalFormatlist.AddDataBarRule(ARange: TsCellRange); +procedure TsConditionalFormatlist.AddDataBarRule(ASheet: TsBasicWorksheet; + ARange: TsCellRange); begin raise Exception.Create('DataBars not yet implemented.'); end; @@ -287,7 +294,7 @@ begin for r := CF.CellRange.Row1 to CF.CellRange.Row2 do for c := CF.CellRange.Col1 to CF.CellRange.Col2 do begin - cell := TsWorksheet(FWorksheet).FindCell(r, c); + cell := TsWorksheet(CF.Worksheet).FindCell(r, c); if Assigned(cell) and (Length(cell^.ConditionalFormatIndex) > 0) then begin for i := AIndex+1 to High(cell^.ConditionalFormatIndex) do cell^.ConditionalFormatIndex[i-1] := cell^.ConditionalFormatIndex[i]; @@ -304,7 +311,8 @@ end; This function searches all format item whether a given cell ranges is already listed. -------------------------------------------------------------------------------} -function TsConditionalFormatList.Find(ARange: TsCellRange): Integer; +function TsConditionalFormatList.Find(ASheet: TsBasicWorksheet; + ARange: TsCellRange): Integer; var i: Integer; CF: TsConditionalFormat; @@ -313,12 +321,15 @@ begin for i := 0 to Count-1 do begin CF := TsConditionalFormat(Items[i]); - CFRange := CF.CellRange; - if (CFRange.Row1 = ARange.Row1) and (CFRange.Row2 = ARange.Row2) and - (CFRange.Col1 = ARange.Col1) and (CFRange.Col2 = ARange.Col2) then + if CF.Worksheet = ASheet then begin - Result := i; - exit; + CFRange := CF.CellRange; + if (CFRange.Row1 = ARange.Row1) and (CFRange.Row2 = ARange.Row2) and + (CFRange.Col1 = ARange.Col1) and (CFRange.Col2 = ARange.Col2) then + begin + Result := i; + exit; + end; end; end; Result := -1; diff --git a/components/fpspreadsheet/source/common/fpspreadsheet.pas b/components/fpspreadsheet/source/common/fpspreadsheet.pas index 19ada966e..1eaf61f62 100644 --- a/components/fpspreadsheet/source/common/fpspreadsheet.pas +++ b/components/fpspreadsheet/source/common/fpspreadsheet.pas @@ -75,7 +75,6 @@ type FHyperlinks: TsHyperlinks; FFormulas: TsFormulas; FImages: TFPList; - FConditionalFormats: TsConditionalFormatList; FRows, FCols: TIndexedAVLTree; // This lists contain only rows or cols with styles different from default FActiveCellRow: Cardinal; FActiveCellCol: Cardinal; @@ -109,7 +108,6 @@ type FOnWriteCellData: TsWorksheetWriteCellDataEvent; { Setter/Getter } - function GetConditionalFormatCount: Integer; function GetDefaultColWidth: Single; function GetDefaultRowHeight: Single; function GetFormatSettings: TFormatSettings; @@ -386,7 +384,6 @@ type AValue: TsCellProtections); overload; { Conditional formatting } - function ReadConditionalFormat(AIndex: Integer): TsConditionalFormat; function WriteConditionalCellFormat(ARange: TsCellRange; ACondition: TsCFCondition; ACellFormatIndex: Integer): Integer; overload; function WriteConditionalCellFormat(ARange: TsCellRange; ACondition: TsCFCondition; @@ -610,8 +607,6 @@ type property Cells: TsCells read FCells; {@@ List of all column records of the worksheet having a non-standard column width } property Cols: TIndexedAVLTree read FCols; - {@@ Count of conditional format entries } - property ConditionalFormatCount: Integer read GetConditionalFormatCount; {@@ Information how the worksheet is encrypted } property CryptoInfo: TsCryptoInfo read FCryptoInfo write FCryptoInfo; {@@ List of all comment records } @@ -741,6 +736,7 @@ type FFontList: TFPList; FNumFormatList: TFPList; FCellFormatList: TsCellFormatList; + FConditionalFormatList: TsConditionalFormatList; FEmbeddedObjList: TFPList; { Internal methods } @@ -825,6 +821,10 @@ type function GetPointerToCellFormat(AIndex: Integer): PsCellFormat; procedure RemoveAllCellFormats(AKeepDefaultFormat: Boolean); + { Conditional formatting } + function GetConditionalFormat(AIndex: Integer): TsConditionalFormat; + function GetNumConditionalFormats: Integer; + { Font handling } function AddFont(const AFontName: String; ASize: Single; AStyle: TsFontStyles; AColor: TsColor; APosition: TsFontPosition = fpNormal): Integer; overload; @@ -1185,7 +1185,6 @@ begin FHyperlinks := TsHyperlinks.Create; FFormulas := TsFormulas.Create; FImages := TFPList.Create; - FConditionalFormats := TsConditionalFormatList.Create; FPageLayout := TsPageLayout.Create(self); @@ -1229,7 +1228,6 @@ begin FHyperlinks.Free; FFormulas.Free; FImages.Free; - FConditionalFormats.Free; inherited Destroy; end; @@ -8705,6 +8703,7 @@ begin FNumFormatList := TsNumFormatList.Create(FormatSettings, true); FCellFormatList := TsCellFormatList.Create(false); + FConditionalFormatList := TsConditionalFormatList.Create; FEmbeddedObjList := TFPList.Create; // Add default cell format @@ -8725,6 +8724,7 @@ begin EnableNotifications; FWorksheets.Free; + FConditionalFormatList.Free; FCellFormatList.Free; FNumFormatList.Free; @@ -9860,6 +9860,19 @@ begin end; +{ Conditional formats } + +function TsWorkbook.GetConditionalFormat(AIndex: Integer): TsConditionalFormat; +begin + Result := FConditionalFormatList[AIndex] as TsConditionalFormat; +end; + +function TsWorkbook.GetNumConditionalFormats: Integer; +begin + Result := FConditionalFormatList.Count; +end; + + { Font handling } {@@ ---------------------------------------------------------------------------- diff --git a/components/fpspreadsheet/source/common/fpspreadsheet_cf.inc b/components/fpspreadsheet/source/common/fpspreadsheet_cf.inc index 3bf378248..042c3963b 100644 --- a/components/fpspreadsheet/source/common/fpspreadsheet_cf.inc +++ b/components/fpspreadsheet/source/common/fpspreadsheet_cf.inc @@ -1,20 +1,5 @@ { Included by fpspreadsheet.pas } -{ Returns the count of conditional format items } -function TsWorksheet.GetConditionalFormatCount: Integer; -begin - Result := FConditionalFormats.Count; -end; - -{@@ ---------------------------------------------------------------------------- - Returns the conditional format item stored in the CF list at the specified - index. --------------------------------------------------------------------------------} -function TsWorksheet.ReadConditionalFormat(AIndex: Integer): TsConditionalFormat; -begin - Result := TsConditionalFormat(FConditionalFormats[AIndex]); -end; - procedure StoreCFIndexInCells(AWorksheet: TsWorksheet; AIndex: Integer; ARange: TsCellRange); var @@ -42,8 +27,8 @@ end; function TsWorksheet.WriteConditionalCellFormat(ARange: TsCellRange; ACondition: TsCFCondition; ACellFormatIndex: Integer): Integer; begin - Result := FConditionalFormats.AddCellRule(ARange, ACondition, - ACellFormatIndex); + Result := FWorkbook.FConditionalFormatList.AddCellRule(Self, ARange, + ACondition, ACellFormatIndex); StoreCFIndexInCells(self, Result, ARange); end; @@ -58,7 +43,7 @@ end; function TsWorksheet.WriteConditionalCellFormat(ARange: TsCellRange; ACondition: TsCFCondition; AParam: Variant; ACellFormatIndex: Integer): Integer; begin - Result := FConditionalFormats.AddCellRule(ARange, ACondition, + Result := FWorkbook.FConditionalFormatList.AddCellRule(Self, ARange, ACondition, AParam, ACellFormatIndex); StoreCFIndexInCells(self, Result, ARange); end; @@ -75,7 +60,7 @@ function TsWorksheet.WriteConditionalCellFormat(ARange: TsCellRange; ACondition: TsCFCondition; AParam1, AParam2: Variant; ACellFormatIndex: Integer): Integer; begin - Result := FConditionalFormats.AddCellRule(ARange, ACondition, + Result := FWorkbook.FConditionalFormatList.AddCellRule(Self, ARange, ACondition, AParam1, AParam2, ACellFormatIndex); StoreCFIndexInCells(self, Result, ARange); end; diff --git a/components/fpspreadsheet/source/common/fpsutils.pas b/components/fpspreadsheet/source/common/fpsutils.pas index 39269d8fd..588b28f93 100644 --- a/components/fpspreadsheet/source/common/fpsutils.pas +++ b/components/fpspreadsheet/source/common/fpsutils.pas @@ -194,6 +194,7 @@ function CombineTextAndRichTextParams(AText: String; procedure SplitTextAndRichTextParams(AValue: String; out AText: String; out ARichText: TsRichTextParams); function SplitStr(const AText: String; ADelimiter: Char): TStringArray; +function SafeQuoteStr(AString: String): String; function UnquoteStr(AString: String): String; function InitSearchParams(ASearchText: String = ''; AOptions: TsSearchOptions = []; @@ -2371,6 +2372,25 @@ begin end; end; +{@@ ---------------------------------------------------------------------------- + Makes sure that the string is encloed by quotes ("). + Avoids duplicate quotation. +-------------------------------------------------------------------------------} +function SafeQuoteStr(AString: String): String; +begin + if AString = '' then + Result := '""' + else + if Length(AString) = 1 then + Result := '"' + AString + '"' + else + begin + Result := AString; + if AString[1] <> '"' then Result := '"' + Result; + if AString[Length(AString)] <> '"' then Result := Result + '"'; + end; +end; + {@@ ---------------------------------------------------------------------------- Removes quotation characters which enclose a string -------------------------------------------------------------------------------} diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas index 959b0a28d..c4cae163b 100644 --- a/components/fpspreadsheet/source/common/xlsxooxml.pas +++ b/components/fpspreadsheet/source/common/xlsxooxml.pas @@ -3081,30 +3081,27 @@ begin SetLength(FDifferentialFormatIndexList, n); book := TsWorkbook(FWorkbook); - for i:=0 to book.GetWorksheetCount-1 do begin - sheet := book.GetWorksheetByIndex(i); - for j := 0 to sheet.ConditionalFormatCount-1 do - begin - CF := sheet.ReadConditionalFormat(j); - for k := 0 to CF.RulesCount-1 do - if CF.Rules[k] is TsCFCellRule then - begin - rule := TsCFCellRule(CF.Rules[k]); - idx := -1; - for d := 0 to High(FDifferentialFormatIndexList) do - if FDifferentialFormatIndexList[d] = rule.FormatIndex then - begin - idx := d; - break; - end; - if idx = -1 then + for j := 0 to book.GetNumConditionalFormats-1 do + begin + CF := book.GetConditionalFormat(j); + for k := 0 to CF.RulesCount-1 do + if CF.Rules[k] is TsCFCellRule then + begin + rule := TsCFCellRule(CF.Rules[k]); + idx := -1; + for d := 0 to High(FDifferentialFormatIndexList) do + if FDifferentialFormatIndexList[d] = rule.FormatIndex then begin - SetLength(FDifferentialFormatIndexList, n+1); - FDifferentialFormatIndexList[n] := rule.FormatIndex; - inc(n); - end; + idx := d; + break; + end; + if idx = -1 then + begin + SetLength(FDifferentialFormatIndexList, n+1); + FDifferentialFormatIndexList[n] := rule.FormatIndex; + inc(n); end; - end; + end; end; end; @@ -3555,23 +3552,29 @@ end; procedure TsSpreadOOXMLWriter.WriteConditionalFormats(AStream: TStream; AWorksheet: TsBasicWorksheet); var + book: TsWorkbook; worksheet: TsWorksheet absolute AWorksheet; i: Integer; CF: TsConditionalFormat; priority: Integer = 0; + ncf: Integer; begin - if worksheet.ConditionalFormatCount = 0 then + book := TsWorkbook(FWorkbook); + ncf := book.GetNumConditionalFormats; + if ncf = 0 then exit; - for i := 0 to worksheet.ConditionalFormatCount-1 do + for i := 0 to ncf-1 do begin - CF := worksheet.ReadConditionalFormat(i); - inc(priority, CF.RulesCount); + CF := book.GetConditionalFormat(i); + if CF.Worksheet = AWorksheet then + inc(priority, CF.RulesCount); end; - for i := 0 to worksheet.ConditionalFormatCount-1 do begin - CF := worksheet.ReadConditionalFormat(i); - WriteConditionalFormat(AStream, CF, priority); + for i := 0 to ncf-1 do begin + CF := book.GetConditionalFormat(i); + if CF.Worksheet = AWorksheet then + WriteConditionalFormat(AStream, CF, priority); end; end;