From ef69632cea644b4f768d6879703932d4fb122b30 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 27 Jul 2015 23:01:52 +0000 Subject: [PATCH] fpspreadsheet: Initial version of searching in workbook/worksheet, not perfect yet. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4214 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/visual/fpsctrls/demo_ctrls.lpi | 10 +- .../examples/visual/fpsctrls/demo_ctrls.lpr | 2 +- .../examples/visual/fpsctrls/main.lfm | 122 ++++++- .../examples/visual/fpsctrls/main.pas | 44 ++- components/fpspreadsheet/fpspreadsheet.pas | 329 +++++++++++++++++- components/fpspreadsheet/fpstypes.pas | 6 + 6 files changed, 473 insertions(+), 40 deletions(-) diff --git a/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpi b/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpi index 4ede2140f..e9d6940ec 100644 --- a/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpi +++ b/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpi @@ -60,7 +60,7 @@ - + @@ -117,6 +117,14 @@ + + + + + + + + diff --git a/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpr b/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpr index 4314a1866..0596bbd90 100644 --- a/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpr +++ b/components/fpspreadsheet/examples/visual/fpsctrls/demo_ctrls.lpr @@ -7,7 +7,7 @@ uses cthreads, {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset - Forms, main, sHyperlinkForm, sNumFormatForm; + Forms, main, sHyperlinkForm, sNumFormatForm, sSearchForm; {$R *.res} diff --git a/components/fpspreadsheet/examples/visual/fpsctrls/main.lfm b/components/fpspreadsheet/examples/visual/fpsctrls/main.lfm index 7be2cda62..0eb128bcf 100644 --- a/components/fpspreadsheet/examples/visual/fpsctrls/main.lfm +++ b/components/fpspreadsheet/examples/visual/fpsctrls/main.lfm @@ -2,10 +2,10 @@ object MainForm: TMainForm Left = 338 Height = 621 Top = 118 - Width = 940 + Width = 997 Caption = 'demo_ctrls' ClientHeight = 601 - ClientWidth = 940 + ClientWidth = 997 Menu = MainMenu ShowHint = True LCLVersion = '1.5' @@ -13,7 +13,7 @@ object MainForm: TMainForm Left = 0 Height = 518 Top = 83 - Width = 654 + Width = 711 TabIndex = 0 Tabs.Strings = ( 'Sheet1' @@ -25,7 +25,7 @@ object MainForm: TMainForm Left = 2 Height = 493 Top = 23 - Width = 650 + Width = 707 FrozenCols = 0 FrozenRows = 0 ReadFormulas = False @@ -50,7 +50,7 @@ object MainForm: TMainForm end end object InspectorTabControl: TTabControl - Left = 659 + Left = 716 Height = 518 Top = 83 Width = 281 @@ -123,7 +123,7 @@ object MainForm: TMainForm end end object InspectorSplitter: TSplitter - Left = 654 + Left = 711 Height = 518 Top = 83 Width = 5 @@ -135,7 +135,7 @@ object MainForm: TMainForm Left = 0 Height = 26 Top = 24 - Width = 940 + Width = 997 AutoSize = True ButtonHeight = 26 ButtonWidth = 24 @@ -400,7 +400,7 @@ object MainForm: TMainForm Left = 0 Height = 24 Top = 0 - Width = 940 + Width = 997 AutoSize = True ButtonHeight = 24 ButtonWidth = 24 @@ -432,7 +432,7 @@ object MainForm: TMainForm Style = tbsDivider end object ToolButton2: TToolButton - Left = 480 + Left = 509 Top = 0 Action = AcFileExit end @@ -561,12 +561,25 @@ object MainForm: TMainForm Caption = 'ToolButton54' Style = tbsDivider end + object ToolButton4: TToolButton + Left = 480 + Top = 0 + Action = AcSearch + end + object ToolButton55: TToolButton + Left = 504 + Height = 24 + Top = 0 + Width = 5 + Caption = 'ToolButton55' + Style = tbsDivider + end end object ToolBar3: TToolBar Left = 0 Height = 28 Top = 50 - Width = 940 + Width = 997 AutoSize = True Caption = 'ToolBar3' Constraints.MinHeight = 28 @@ -597,7 +610,7 @@ object MainForm: TMainForm Left = 144 Height = 24 Top = 0 - Width = 796 + Width = 853 Align = alClient BorderSpacing.Bottom = 2 TabOrder = 1 @@ -616,7 +629,7 @@ object MainForm: TMainForm Left = 0 Height = 5 Top = 78 - Width = 940 + Width = 997 Align = alTop ResizeAnchor = akTop end @@ -664,6 +677,7 @@ object MainForm: TMainForm Caption = 'E&xit' Hint = 'Exit' ImageIndex = 0 + ShortCut = 32856 end object AcFontBold: TsFontStyleAction Category = 'FPSpreadsheet' @@ -1540,12 +1554,20 @@ object MainForm: TMainForm Hint = 'Delete hyperlink from selected cell' ImageIndex = 58 end + object AcSearch: TAction + Category = 'Edit' + Caption = 'Search...' + Hint = 'Search for cells' + ImageIndex = 70 + OnExecute = AcSearchExecute + ShortCut = 16454 + end end object ImageList: TImageList left = 176 top = 312 Bitmap = { - 4C69460000001000000010000000003F9300003F9300003F9300003F9424003F + 4C69470000001000000010000000003F9300003F9300003F9300003F9424003F 948A003E93CC004095CC004095CC004095CC004095CC004095CC004095CC0040 95CC004095CC00409599003F9400003F9300003F9324003F938A0E4B9CD33F76 C0EC5D90D4FF3365A9FFA0A0A0FFA9A9A9FFA9A9A9FFAAAAAAFFACACACFFAEAE @@ -3785,6 +3807,38 @@ object MainForm: TMainForm 6FFF4C9750FB529C56344E995222FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0060A664315BA25FCC569F 5A4BFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FCDEC102FADCBF97F9D9 + BBE3F6D6B8FDF4D3B4FDF1CFAFE3EECBAB97EBC6A602FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FCDEC002FADBBEC0F9E2CDFFFAEC + DEFFF9EEE2FFF9EDE2FFF8E9DAFFF0D5BDFFE7C09FC0E3BC9A02FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FADBBD97F8E2CCFFFAEEE3FFF7E7 + D6FFF6E2CEFFF6E1CBFFF6E3D0FFF9EADDFFECCFB5FFDFB69397FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F7D7B9E3F9EBDEFFF7E7D6FFF6E1 + CCFFF5E0CAFFF5DEC8FFF5DDC5FFF6E1CBFFF5E2D0FFDBB08CE3FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F4D3B4FDF9EDE1FFF6E1CCFFF5DF + C9FFF5DEC7FFF4DCC4FFF4DBC2FFF4DAC0FFF8E7D6FFD7AA86FDFFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F0CEAEFDF9ECDFFFF5DFC8FFF5DD + C6FFF4DCC3FFF4DAC1FFF3D9BEFFF3D7BDFFF8E6D3FFD3A57FFDFFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00ECC8A8E3F7E7D7FFF6E1CCFFF4DB + C2FFF4DAC0FFF3D8BDFFF3D7BBFFF4DBC2FFF3DEC9FFCD9F7BE7FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E8C3A297EDD0B7FFF8E8D9FFF5DE + C8FFF3D8BDFFF3D6BBFFF4DBC2FFF7E4D2FFDFBB9DFF9D9492F74B84BC27FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E4BD9B02E1B896C0E8C9AEFFF5E1 + CDFFF7E5D3FFF7E5D1FFF3DDC8FFDFBA9CFFC7A891FF86AED5FF417DB5EB3977 + AF27FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00DDB28F02D9AE8A97D6A9 + 85E3D3A57FFDD0A07BFDCD9C76E4A2938ADE75A2CCFFABCBE8FF76A4CEFF3070 + A8EB286BA327FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00447FB7063C79B1AD6497C5FF9DC1E4FF6699 + C7FF1F659DEBFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003272AA062B6DA5AD558DBCFF89B5 + DDFF185F97FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0022669E061B629AAD2267 + 9DFF115B9387FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00145D9503105A + 921AFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF FF00FFFFFF00FFFFFF00FFFFFF00 } end @@ -4337,6 +4391,48 @@ object MainForm: TMainForm } end end + object MenuItem131: TMenuItem + Caption = '-' + end + object MenuItem132: TMenuItem + Action = AcSearch + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000064000000640000000000000000000000FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00145D9503105A921AFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF0022669E061B629AAD22679DFF115B9387FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003272 + AA062B6DA5AD558DBCFF89B5DDFF185F97FFFFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00447FB7063C79 + B1AD6497C5FF9DC1E4FF6699C7FF1F659DEBFFFFFF00FFFFFF00FFFFFF00FFFF + FF00DDB28F02D9AE8A97D6A985E3D3A57FFDD0A07BFDCD9C76E4A2938ADE75A2 + CCFFABCBE8FF76A4CEFF3070A8EB286BA327FFFFFF00FFFFFF00FFFFFF00E4BD + 9B02E1B896C0E8C9AEFFF5E1CDFFF7E5D3FFF7E5D1FFF3DDC8FFDFBA9CFFC7A8 + 91FF86AED5FF417DB5EB3977AF27FFFFFF00FFFFFF00FFFFFF00FFFFFF00E8C3 + A297EDD0B7FFF8E8D9FFF5DEC8FFF3D8BDFFF3D6BBFFF4DBC2FFF7E4D2FFDFBB + 9DFF9D9492F74B84BC27FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00ECC8 + A8E3F7E7D7FFF6E1CCFFF4DBC2FFF4DAC0FFF3D8BDFFF3D7BBFFF4DBC2FFF3DE + C9FFCD9F7BE7FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F0CE + AEFDF9ECDFFFF5DFC8FFF5DDC6FFF4DCC3FFF4DAC1FFF3D9BEFFF3D7BDFFF8E6 + D3FFD3A57FFDFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F4D3 + B4FDF9EDE1FFF6E1CCFFF5DFC9FFF5DEC7FFF4DCC4FFF4DBC2FFF4DAC0FFF8E7 + D6FFD7AA86FDFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F7D7 + B9E3F9EBDEFFF7E7D6FFF6E1CCFFF5E0CAFFF5DEC8FFF5DDC5FFF6E1CBFFF5E2 + D0FFDBB08CE3FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FADB + BD97F8E2CCFFFAEEE3FFF7E7D6FFF6E2CEFFF6E1CBFFF6E3D0FFF9EADDFFECCF + B5FFDFB69397FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FCDE + C002FADBBEC0F9E2CDFFFAECDEFFF9EEE2FFF9EDE2FFF8E9DAFFF0D5BDFFE7C0 + 9FC0E3BC9A02FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FCDEC102FADCBF97F9D9BBE3F6D6B8FDF4D3B4FDF1CFAFE3EECBAB97EBC6 + A602FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 + } + end end object MnuFormat: TMenuItem Caption = 'Format' diff --git a/components/fpspreadsheet/examples/visual/fpsctrls/main.pas b/components/fpspreadsheet/examples/visual/fpsctrls/main.pas index e4279bdca..7f2d2e4bb 100644 --- a/components/fpspreadsheet/examples/visual/fpsctrls/main.pas +++ b/components/fpspreadsheet/examples/visual/fpsctrls/main.pas @@ -21,6 +21,7 @@ type AcSettingsCSVParams: TAction; AcSettingsCurrency: TAction; AcSettingsFormatSettings: TAction; + AcSearch: TAction; AcViewInspector: TAction; ActionList: TActionList; AcFileExit: TFileExit; @@ -61,6 +62,8 @@ type MenuItem128: TMenuItem; MenuItem129: TMenuItem; MenuItem130: TMenuItem; + MenuItem131: TMenuItem; + MenuItem132: TMenuItem; MnuSettings: TMenuItem; MenuItem11: TMenuItem; MenuItem12: TMenuItem; @@ -299,6 +302,7 @@ type ToolButton38: TToolButton; ToolButton39: TToolButton; TbCommentAdd: TToolButton; + ToolButton4: TToolButton; ToolButton40: TToolButton; ToolButton41: TToolButton; ToolButton42: TToolButton; @@ -317,6 +321,7 @@ type ToolButton52: TToolButton; ToolButton53: TToolButton; ToolButton54: TToolButton; + ToolButton55: TToolButton; ToolButton6: TToolButton; ToolButton7: TToolButton; ToolButton8: TToolButton; @@ -333,6 +338,7 @@ type AWorkbook: TsWorkbook; var ANumFormatStr: String); procedure AcRowAddExecute(Sender: TObject); procedure AcRowDeleteExecute(Sender: TObject); + procedure AcSearchExecute(Sender: TObject); procedure AcSettingsCSVParamsExecute(Sender: TObject); procedure AcSettingsCurrencyExecute(Sender: TObject); procedure AcSettingsFormatSettingsExecute(Sender: TObject); @@ -344,6 +350,7 @@ type const AHyperlink: TsHyperlink); private { private declarations } + procedure SearchFound(Sender: TObject; ACell: PCell); procedure UpdateCaption; protected procedure ReadFromIni; @@ -364,7 +371,7 @@ uses LCLIntf, inifiles, uriparser, fpsUtils, fpsCSV, sCSVParamsForm, sCurrencyForm, sFormatSettingsForm, sSortParamsForm, - sHyperlinkForm, sNumFormatForm; + sHyperlinkForm, sNumFormatForm, sSearchForm; function CreateIni: TCustomIniFile; begin @@ -476,6 +483,14 @@ begin WorksheetGrid.Row := r; end; +procedure TMainForm.AcSearchExecute(Sender: TObject); +begin + if SearchForm = nil then + SearchForm := TSearchForm.Create(self); + SearchForm.OnFound := @SearchFound; + SearchForm.Execute(WorkbookSource.Workbook, DefaultSearchParams); +end; + procedure TMainForm.AcSettingsCSVParamsExecute(Sender: TObject); var F: TCSVParamsForm; @@ -568,6 +583,22 @@ begin end; end; +procedure TMainForm.SearchFound(Sender: TObject; ACell: PCell); +begin + // There could be status message "search string found", here +end; + +procedure TMainForm.UpdateCaption; +begin + if WorkbookSource = nil then + Caption := 'demo_ctrls' + else + Caption := Format('demo_ctrls - "%s" [%s]', [ + AnsiToUTF8(WorkbookSource.Filename), + GetFileFormatName(WorkbookSource.Workbook.FileFormat) + ]); +end; + procedure TMainForm.WriteToIni; var ini: TCustomIniFile; @@ -596,16 +627,5 @@ begin end; end; -procedure TMainForm.UpdateCaption; -begin - if WorkbookSource = nil then - Caption := 'demo_ctrls' - else - Caption := Format('demo_ctrls - "%s" [%s]', [ - AnsiToUTF8(WorkbookSource.Filename), - GetFileFormatName(WorkbookSource.Workbook.FileFormat) - ]); -end; - end. diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index c3bcb9ebe..cbf3b3d9c 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -22,6 +22,7 @@ unit fpspreadsheet; {$ifdef fpc} {$mode delphi}{$H+} +// {$mode objpas}{$H+} {$endif} {$include fps.inc} @@ -389,6 +390,11 @@ type function GetCell(AddressStr: String): PCell; overload; function GetCellCount: Cardinal; + function FindNextCellInCol(ARow, ACol: Cardinal): PCell; + function FindNextCellInRow(ARow, ACol: Cardinal): PCell; + function FindPrevCellInCol(ARow, ACol: Cardinal): PCell; + function FindPrevCellInRow(ARow, ACol: Cardinal): PCell; + function GetFirstColIndex(AForceCalculation: Boolean = false): Cardinal; function GetLastColIndex(AForceCalculation: Boolean = false): Cardinal; function GetLastColNumber: Cardinal; deprecated 'Use GetLastColIndex'; @@ -434,6 +440,10 @@ type function GetSelectionCount: Integer; procedure SetSelection(const ASelection: TsCellRangeArray); + // Searching + function Search(ASearchText: String; AOptions: TsSearchOptions; + AStartRow: Cardinal = $FFFFFFFF; AStartCol: Cardinal = $FFFFFFFF): PCell; + // Comments function FindComment(ACell: PCell): PsComment; function HasComment(ACell: PCell): Boolean; @@ -711,6 +721,11 @@ type function UsesColor(AColorIndex: TsColor): Boolean; *) + { Searching } + function Search(ASearchText: String; AOptions: TsSearchOptions; + AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = $FFFFFFFF; + AStartCol: Cardinal = $FFFFFFFF): PCell; + { Utilities } procedure UpdateCaches; @@ -806,7 +821,7 @@ procedure CopyCellFormat(AFromCell, AToCell: PCell); implementation uses - Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser, + Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser, RegExpr, fpsStrings, uvirtuallayer_ole, fpsUtils, fpsreaderwriter, fpsCurrency, fpsExprParser, fpsNumFormatParser; @@ -860,8 +875,8 @@ var begin Assert(AFromCell <> nil); Assert(AToCell <> nil); - sourceSheet := TsWorksheet(AFromCell.Worksheet); - destSheet := TsWorksheet(AToCell.Worksheet); + sourceSheet := TsWorksheet(AFromCell^.Worksheet); + destSheet := TsWorksheet(AToCell^.Worksheet); if (sourceSheet=nil) or (destSheet=nil) or (sourceSheet.Workbook = destSheet.Workbook) then AToCell^.FormatIndex := AFromCell^.FormatIndex else @@ -891,19 +906,19 @@ end; function CompareCells(Item1, Item2: Pointer): Integer; begin - result := LongInt(PCell(Item1).Row) - PCell(Item2).Row; + result := LongInt(PCell(Item1)^.Row) - PCell(Item2)^.Row; if Result = 0 then - Result := LongInt(PCell(Item1).Col) - PCell(Item2).Col; + Result := LongInt(PCell(Item1)^.Col) - PCell(Item2)^.Col; end; function CompareRows(Item1, Item2: Pointer): Integer; begin - Result := LongInt(PRow(Item1).Row) - PRow(Item2).Row; + Result := LongInt(PRow(Item1)^.Row) - PRow(Item2)^.Row; end; function CompareCols(Item1, Item2: Pointer): Integer; begin - Result := LongInt(PCol(Item1).Col) - PCol(Item2).Col; + Result := LongInt(PCol(Item1)^.Col) - PCol(Item2)^.Col; end; function CompareMergedCells(Item1, Item2: Pointer): Integer; @@ -1938,6 +1953,56 @@ begin Result := nil; end; +function TsWorksheet.FindNextCellInCol(ARow, ACol: Cardinal): PCell; +var + last: Cardinal; +begin + last := GetLastRowIndex; + if ARow = last then + Result := nil + else + repeat + inc(ARow); + Result := FindCell(ARow, ACol); + until (Result <> nil) or (ARow = last); +end; + +function TsWorksheet.FindNextCellInRow(ARow, ACol: Cardinal): PCell; +var + last: Cardinal; +begin + last := GetLastColIndex; + if ACol = last then + Result := nil + else + Repeat + inc(ACol); + Result := Findcell(ARow, ACol); + until (Result <> nil) or (ACol = last); +end; + +function TsWorksheet.FindPrevCellInCol(ARow, ACol: Cardinal): PCell; +begin + if ARow = 0 then + Result := nil + else + repeat + dec(ARow); + Result := FindCell(ARow, ACol); + until (Result <> nil) or (ARow = 0); +end; + +function TsWorksheet.FindPrevCellInRow(ARow, ACol: Cardinal): PCell; +begin + if ACol = 0 then + Result := nil + else + repeat + dec(ACol); + Result := FindCell(ARow, ACol); + until (Result <> nil) or (ACol = 0); +end; + {@@ ---------------------------------------------------------------------------- Obtains an allocated cell at the desired location. @@ -2732,7 +2797,7 @@ begin if ACell <> nil then begin fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex); - for b in fmt.Border do + for b in fmt^.Border do Result[b] := fmt^.BorderStyles[b]; end; end; @@ -2812,7 +2877,7 @@ begin fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex); if (uffNumberFormat in fmt^.UsedFormattingFields) then begin - numFmt := Workbook.GetNumberFormat(fmt.NumberFormatIndex); + numFmt := Workbook.GetNumberFormat(fmt^.NumberFormatIndex); if numFmt <> nil then begin ANumFormat := numFmt.NumFormat; @@ -3468,6 +3533,185 @@ begin FSelection[i] := ASelection[i]; end; +{@@ ---------------------------------------------------------------------------- + Searches the cell containing a specified text. The search begins with the + cell "AStartCell". A set of options is respected. Returns a pointer to the + first cell meeting the criteria. +-------------------------------------------------------------------------------} +function TsWorksheet.Search(ASearchText: String; AOptions: TsSearchOptions; + AStartRow: Cardinal = $FFFFFFFF; AStartCol: Cardinal = $FFFFFFFF): PCell; +var + regex: TRegExpr; + cell, startCell: PCell; + r, c: Cardinal; + firstR, firstC, lastR, lastC: Cardinal; + + function CellMatches(ACell: PCell): boolean; + var + txt: String; + begin + txt := ReadAsUTF8Text(ACell); + if (soRegularExpr in AOptions) then + Result := regex.Exec(txt) + else + if (soIgnoreCase in AOptions) then + txt := UTF8Lowercase(txt); + if (soCompareFullCell in AOptions) then + exit(txt = ASearchText); + if UTF8Pos(ASearchText, txt) > 0 then + exit(true); + Result := false; + end; + +begin + Result := nil; + regex := nil; + firstR := 0; + firstC := 0; + lastR := GetLastRowIndex; + lastC := GetLastColIndex; + + // Find first occupied cell to start with + if (soBackward in AOptions) then + begin + if AStartRow = $FFFFFFFF then AStartRow := lastR; + if AStartCol = $FFFFFFFF then AStartCol := lastC; + end else + begin + if AStartRow = $FFFFFFFF then AStartRow := firstR; + if AStartCol = $FFFFFFFF then AStartCol := firstC; + end; + startcell := FindCell(AStartRow, AStartCol); + if startcell = nil then + // Backward search along rows + if (AOptions * [soBackward, soAlongRows] = [soBackward, soAlongRows]) then + begin + startcell := FindPrevCellInRow(AStartRow, AStartCol); + // Not found in this row? Go to previous row + while (startcell = nil) and (AStartRow > 0) do begin + AStartCol := lastC; + dec(AStartRow); + startcell := FindPrevCellInRow(AStartRow, AStartCol); + end; + end + else + // Backward search along columns + if (AOptions * [soBackward, soAlongRows] = [soBackward]) then + begin + startcell := FindPrevCellInCol(AStartRow, AStartCol); + // not found in this column? Go to previous column. + while (startcell = nil) and (AStartcol > 0) do begin + AStartRow := lastR; + dec(AStartCol); + startcell := FindPrevCellInCol(AStartRow, AStartCol); + end; + end + else + // Forward search along rows + if (AOptions * [soBackward, soAlongRows] = [soAlongRows]) then + begin + startcell := FindNextCellInRow(AStartRow, AStartCol); + // Not found in this row? Proceed to next row + while (startcell = nil) and (AStartRow <= lastR) do begin + AStartCol := firstC; + inc(AStartRow); + startcell := FindNextCellInRow(AStartRow, AStartCol); + end; + end + else + // Forward search along columns + if (AOptions * [soBackward, soAlongRows] = []) then + begin + startCell := FindNextCellInCol(AStartRow, AStartCol); + // Not found in this column? Proceed to next column + while (startcell = nil) and (AStartCol <= lastC) do begin + AStartRow := firstR; + inc(AStartCol); + startcell := FindNextCellinCol(AStartRow, AStartCol); + end; + end; + + // Still no occupied cell found for starting? Nothing to do... + if startcell = nil then + exit; + + // Iterate through cells in order defined by the search options + try + if soRegularExpr in AOptions then + begin + regex := TRegExpr.Create; + regex.Expression := ASearchText + end else + if soIgnoreCase in AOptions then + ASearchText := UTF8Lowercase(ASearchText); + + // Perform backward search along rows + if (AOptions * [soBackward, soAlongRows] = [soBackward, soAlongRows]) then + begin + r := startCell^.Row; + for cell in Cells.GetReverseRowEnumerator(r, startCell^.Col) do + if CellMatches(cell) then exit(cell); + if r = 0 then + exit; + while r > 0 do begin + dec(r); + for cell in Cells.GetReverseRowEnumerator(r) do + if CellMatches(cell) then exit(cell); + end; + end + else + // Perform forward search along rows + if (AOptions * [soBackward, soAlongRows] = [soAlongRows]) then + begin + r := startCell^.Row; + for cell in Cells.GetRowEnumerator(r, startCell^.Col) do + if CellMatches(cell) then exit(cell); + if r = lastR then + exit; + while (r < lastR) do + begin + inc(r); + for cell in Cells.GetRowEnumerator(r) do + if CellMatches(cell) then exit(cell); + end; + end + else + // Perform backward search along columns + if (AOptions * [soBackward, soAlongRows] = [soBackward]) then + begin + c := startCell^.Col; + for cell in Cells.GetReverseColEnumerator(c, 0, startCell^.Row) do + if CellMatches(cell) then exit(cell); + if c = 0 then + exit; + while (c > 0) do + begin + dec(c); + for cell in Cells.GetReverseColEnumerator(c) do + if CellMatches(cell) then exit(cell); + end; + end + else + // Perform forward search along columns + if (AOptions * [soBackward, soAlongRows] = []) then + begin + c := startCell^.Col; + for cell in Cells.GetColEnumerator(c, startCell^.Row) do + if CellMatches(cell) then exit(cell); + if c = lastC then + exit; + while (c < lastC) do + begin + inc(c); + for cell in Cells.GetColEnumerator(c) do + if CellMatches(cell) then exit(cell); + end; + end; + finally + if regex <> nil then regex.Free; + end; +end; + {@@ ---------------------------------------------------------------------------- Helper method to update internal caching variables -------------------------------------------------------------------------------} @@ -6096,7 +6340,7 @@ end; -------------------------------------------------------------------------------} procedure TsWorkbook.PrepareBeforeSaving; var - sheet: TsWorksheet; + sheet: pointer; begin // Clear error log FLog.Clear; @@ -6107,7 +6351,7 @@ begin // Calculated formulas (if requested) if (boCalcBeforeSaving in FOptions) then for sheet in FWorksheets do - sheet.CalcFormulas; + TsWorksheet(sheet).CalcFormulas; // Abort if virtual mode is active without an event handler if (boVirtualMode in FOptions) and not Assigned(FOnWriteCellData) then @@ -6119,10 +6363,10 @@ end; -------------------------------------------------------------------------------} procedure TsWorkbook.Recalc; var - sheet: TsWorksheet; + sheet: pointer; begin for sheet in FWorksheets do - sheet.CalcFormulas; + TsWorksheet(sheet).CalcFormulas; end; {@@ ---------------------------------------------------------------------------- @@ -6134,6 +6378,65 @@ begin TsWorksheet(data).Free; end; +{@@ ---------------------------------------------------------------------------- + Searches the entire workbook for the first cell (after AStartCell) containing + a specified text. +-------------------------------------------------------------------------------} +function TsWorkbook.Search(ASearchText: String; AOptions: TsSearchOptions; + AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = $FFFFFFFF; + AStartCol: Cardinal = $FFFFFFFF): PCell; +var + i, idxSheet: Integer; + sheet: TsWorksheet; +begin + // Setup missing default parameters + if soBackward in AOptions then + begin + if (AStartRow = $FFFFFFFF) and (AStartCol = $FFFFFFFF) and (AStartSheet = nil) + then AStartsheet := GetWorksheetByIndex(GetWorksheetCount-1); + if AStartRow = $FFFFFFFF then + AStartRow := AStartsheet.GetLastRowIndex; + if AStartCol = $FFFFFFFF then + AStartCol := AStartsheet.GetLastColIndex; + end else + begin + if (AStartRow = $FFFFFFFF) and (AStartCol = $FFFFFFFF) and (AStartSheet = nil) + then AStartsheet := GetWorksheetByIndex(0); + if (AStartRow = $FFFFFFFF) then + AStartRow := AStartsheet.GetFirstRowIndex; + if (AStartCol = $FFFFFFFF) then + AStartCol := AStartsheet.GetFirstColIndex; + end; + if AStartSheet = nil then + AStartSheet := ActiveWorksheet; + + // Search this worksheet + Result := AStartSheet.Search(ASearchText, AOptions, AStartRow, AStartCol); + if Result <> nil then + exit; + + // If not found continue with other sheets in requested order... + idxSheet := GetWorksheetIndex(AStartSheet); + if (soBackward in AOptions) then + // ... backward + for i := idxSheet - 1 downto 0 do + begin + sheet := GetWorksheetByIndex(i); + Result := sheet.Search(ASearchText, AOptions); + if Result <> nil then + exit; + end + else + // ... forward + for i := idxSheet + 1 to GetWorksheetCount-1 do + begin + sheet := GetWorksheetByIndex(i); + Result := sheet.Search(ASearchText, AOptions); + if Result <> nil then + exit; + end; +end; + {@@ ---------------------------------------------------------------------------- Helper method to update internal caching variables -------------------------------------------------------------------------------} diff --git a/components/fpspreadsheet/fpstypes.pas b/components/fpspreadsheet/fpstypes.pas index ee9121a48..b83e9a9c6 100644 --- a/components/fpspreadsheet/fpstypes.pas +++ b/components/fpspreadsheet/fpstypes.pas @@ -676,6 +676,12 @@ type {@@ Pointer to a page layout record } PsPageLayout = ^TsPageLayout; + {@@ Search option } + TsSearchOption = (soCompareFullCell, soIgnoreCase, soRegularExpr, + soBackward, soAlongRows); + TsSearchOptions = set of TsSearchOption; + + const {@@ Indexes to be used for the various headers and footers } HEADER_FOOTER_INDEX_FIRST = 0;