diff --git a/components/fpspreadsheet/examples/visual/fpsctrls/main.pas b/components/fpspreadsheet/examples/visual/fpsctrls/main.pas index 31d947c13..166f88d6b 100644 --- a/components/fpspreadsheet/examples/visual/fpsctrls/main.pas +++ b/components/fpspreadsheet/examples/visual/fpsctrls/main.pas @@ -355,7 +355,9 @@ type const AHyperlink: TsHyperlink); private { private declarations } - procedure SearchFound(Sender: TObject; ACell: PCell); + procedure SearchClose(Sender: TObject; var CloseAction: TCloseAction); + procedure SearchFound(Sender: TObject; AFound: Boolean; + AWorksheet: TsWorksheet; ARow, ACol: Cardinal); procedure UpdateCaption; protected procedure ReadFromIni; @@ -496,7 +498,9 @@ begin if SearchForm = nil then SearchForm := TSearchForm.Create(self); SearchForm.OnFound := @SearchFound; - SearchForm.Execute(WorkbookSource.Workbook, DefaultSearchParams); + SearchForm.OnClose := @SearchClose; + SearchForm.SearchParams := DefaultSearchParams; + SearchForm.Execute(WorkbookSource.Workbook); end; procedure TMainForm.AcSettingsCSVParamsExecute(Sender: TObject); @@ -601,9 +605,30 @@ begin end; end; -procedure TMainForm.SearchFound(Sender: TObject; ACell: PCell); +procedure TMainForm.SearchClose(Sender: TObject; var CloseAction: TCloseAction); begin - // There could be status message "search string found", here + Unused(CloseAction); + DefaultSearchParams := TSearchForm(Sender).SearchParams; +end; + +procedure TMainForm.SearchFound(Sender: TObject; AFound: Boolean; + AWorksheet: TsWorksheet; ARow, ACol: Cardinal); +begin + Unused(AWorksheet, ARow, ACol); + + if AFound then + begin + // + end + else + begin + DefaultSearchParams := TSearchForm(Sender).SearchParams; + MessageDlg( + Format('The search text "%s" could not be found.', [DefaultSearchParams.SearchText]), + mtInformation, + [mbOK], 0 + ); + end; end; procedure TMainForm.UpdateCaption; diff --git a/components/fpspreadsheet/examples/visual/shared/ssearchform.lfm b/components/fpspreadsheet/examples/visual/shared/ssearchform.lfm index 17f996fd9..4cb732b31 100644 --- a/components/fpspreadsheet/examples/visual/shared/ssearchform.lfm +++ b/components/fpspreadsheet/examples/visual/shared/ssearchform.lfm @@ -1,12 +1,12 @@ object SearchForm: TSearchForm Left = 238 - Height = 271 + Height = 272 Top = 157 - Width = 392 + Width = 483 BorderStyle = bsDialog Caption = 'Search' - ClientHeight = 271 - ClientWidth = 392 + ClientHeight = 272 + ClientWidth = 483 FormStyle = fsStayOnTop OnClose = FormClose OnCreate = FormCreate @@ -24,16 +24,16 @@ object SearchForm: TSearchForm Left = 93 Height = 23 Top = 14 - Width = 283 + Width = 374 Anchors = [akTop, akLeft, akRight] ItemHeight = 15 TabOrder = 0 end object CgSearchOptions: TCheckGroup - Left = 19 + Left = 16 Height = 163 Top = 53 - Width = 189 + Width = 192 AutoFill = True Caption = 'Search options' ChildSizing.LeftRightSpacing = 6 @@ -45,24 +45,24 @@ object SearchForm: TSearchForm ChildSizing.Layout = cclLeftToRightThenTopToBottom ChildSizing.ControlsPerLine = 1 ClientHeight = 143 - ClientWidth = 185 + ClientWidth = 188 Items.Strings = ( - 'Compare full cell ' - 'Ignore case' + 'Compare entire cell ' + 'Match case' 'Regular expression' - 'Backwards' 'Search along rows' + 'Continue at start/end' ) TabOrder = 1 Data = { 050000000202020202 } end - object RgSearchSource: TRadioGroup - Left = 237 - Height = 75 + object RgSearchWithin: TRadioGroup + Left = 232 + Height = 67 Top = 53 - Width = 139 + Width = 232 AutoFill = True Caption = 'Search within' ChildSizing.LeftRightSpacing = 6 @@ -70,39 +70,43 @@ object SearchForm: TSearchForm ChildSizing.EnlargeVertical = crsHomogenousChildResize ChildSizing.ShrinkHorizontal = crsScaleChilds ChildSizing.ShrinkVertical = crsScaleChilds - ChildSizing.Layout = cclLeftToRightThenTopToBottom - ChildSizing.ControlsPerLine = 1 - ClientHeight = 55 - ClientWidth = 135 + ChildSizing.Layout = cclTopToBottomThenLeftToRight + ChildSizing.ControlsPerLine = 2 + ClientHeight = 47 + ClientWidth = 228 + ColumnLayout = clVerticalThenHorizontal + Columns = 2 ItemIndex = 0 Items.Strings = ( - 'worksheet' 'workbook' + 'worksheet' + 'column' + 'row' ) TabOrder = 2 end object ButtonPanel: TPanel Left = 0 Height = 38 - Top = 233 - Width = 392 + Top = 234 + Width = 483 Align = alBottom BevelOuter = bvNone ClientHeight = 38 - ClientWidth = 392 + ClientWidth = 483 TabOrder = 3 object Bevel1: TBevel Left = 6 Height = 3 Top = 0 - Width = 380 + Width = 471 Align = alTop BorderSpacing.Left = 6 BorderSpacing.Right = 6 Shape = bsTopLine end object BtnSearchBack: TBitBtn - Left = 149 + Left = 240 Height = 25 Top = 7 Width = 75 @@ -149,18 +153,19 @@ object SearchForm: TSearchForm Visible = False end object BtnClose: TBitBtn - Left = 309 + Left = 400 Height = 25 Top = 7 Width = 75 Anchors = [akTop, akRight] + Cancel = True DefaultCaption = True Kind = bkClose ModalResult = 11 TabOrder = 1 end object BtnSearch: TBitBtn - Left = 229 + Left = 320 Height = 25 Top = 7 Width = 75 @@ -208,10 +213,10 @@ object SearchForm: TSearchForm end end object RgSearchStart: TRadioGroup - Left = 237 - Height = 75 - Top = 141 - Width = 139 + Left = 232 + Height = 56 + Top = 160 + Width = 232 AutoFill = True Caption = 'Start search at' ChildSizing.LeftRightSpacing = 6 @@ -220,13 +225,14 @@ object SearchForm: TSearchForm ChildSizing.ShrinkHorizontal = crsScaleChilds ChildSizing.ShrinkVertical = crsScaleChilds ChildSizing.Layout = cclLeftToRightThenTopToBottom - ChildSizing.ControlsPerLine = 1 - ClientHeight = 55 - ClientWidth = 135 + ChildSizing.ControlsPerLine = 2 + ClientHeight = 36 + ClientWidth = 228 + Columns = 2 ItemIndex = 0 Items.Strings = ( - 'beginning/end' 'active cell' + 'beginning/end' ) TabOrder = 4 end diff --git a/components/fpspreadsheet/examples/visual/shared/ssearchform.pas b/components/fpspreadsheet/examples/visual/shared/ssearchform.pas index b46720471..e4e1a1623 100644 --- a/components/fpspreadsheet/examples/visual/shared/ssearchform.pas +++ b/components/fpspreadsheet/examples/visual/shared/ssearchform.pas @@ -5,25 +5,12 @@ unit sSearchForm; interface uses - Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ButtonPanel, - StdCtrls, ExtCtrls, Buttons, fpsTypes, fpspreadsheet; + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, + StdCtrls, ExtCtrls, Buttons, fpsTypes, fpspreadsheet, fpsSearch; type - - { TSearchParams } - - TsSearchSource = (spsWorksheet, spsWorkbook); - TsSearchStart = (spsBeginningEnd, spsActiveCell); - - TsSearchParams = record - SearchText: String; - Options: TsSearchOptions; - Source: TsSearchSource; - Start: TsSearchStart; - end; - - TsSearchEvent = procedure (Sender: TObject; ACell: PCell) of object; - + TsSearchEvent = procedure (Sender: TObject; AFound: Boolean; + AWorksheet: TsWorksheet; ARow, ACol: Cardinal) of object; { TSearchForm } @@ -37,24 +24,25 @@ type LblSearchText: TLabel; ButtonPanel: TPanel; RgSearchStart: TRadioGroup; - RgSearchSource: TRadioGroup; + RgSearchWithin: TRadioGroup; procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormCreate(Sender: TObject); procedure FormShow(Sender: TObject); procedure SearchButtonClick(Sender: TObject); private { private declarations } + FSearchEngine: TsSearchEngine; FWorkbook: TsWorkbook; - FFoundCell: PCell; + FFoundWorksheet: TsWorksheet; + FFoundRow, FFoundCol: Cardinal; FOnFound: TsSearchEvent; - procedure CtrlsToParams(var ASearchParams: TsSearchParams); - function FindStartCell(AParams: TsSearchParams; var AWorksheet: TsWorksheet; - var AStartRow, AStartCol: Cardinal): Boolean; - procedure ParamsToCtrls(const ASearchParams: TsSearchParams); + function GetParams: TsSearchParams; + procedure SetParams(const AValue: TsSearchParams); public { public declarations } - procedure Execute(AWorkbook: TsWorkbook; var ASearchParams: TsSearchParams); + procedure Execute(AWorkbook: TsWorkbook); property Workbook: TsWorkbook read FWorkbook; + property SearchParams: TsSearchParams read GetParams write SetParams; property OnFound: TsSearchEvent read FOnFound write FOnFound; end; @@ -63,9 +51,8 @@ var DefaultSearchParams: TsSearchParams = ( SearchText: ''; - Options: [soIgnoreCase]; - Source: spsWorksheet; - Start: spsActiveCell; + Options: []; + Within: swWorksheet ); @@ -73,26 +60,33 @@ implementation {$R *.lfm} -const - MAX_SEARCH_ITEMS = 10; +uses + fpsUtils; -procedure TSearchForm.CtrlsToParams(var ASearchParams: TsSearchParams); -var - i: Integer; +const + MAX_SEARCH_ITEMS = 10; + + COMPARE_ENTIRE_CELL = 0; + MATCH_CASE = 1; + REGULAR_EXPRESSION = 2; + SEARCH_ALONG_ROWS = 3; + CONTINUE_AT_START_END = 4; + + +{ TSearchForms } + +procedure TSearchForm.Execute(AWorkbook: TsWorkbook); begin - ASearchParams.SearchText := CbSearchText.Text; - ASearchParams.Options := []; - for i:=0 to CgSearchOptions.Items.Count-1 do - if CgSearchOptions.Checked[i] then - Include(ASearchparams.Options, TsSearchOption(i)); - ASearchParams.Source := TsSearchSource(RgSearchSource.ItemIndex); - ASearchParams.Start := TsSearchStart(RgSearchStart.ItemIndex); + FWorkbook := AWorkbook; + Show; end; procedure TSearchForm.FormClose(Sender: TObject; var CloseAction: TCloseAction); var P: TPoint; begin + Unused(CloseAction); + FreeAndNil(FSearchEngine); P.X := Left; P.Y := Top; Position := poDesigned; @@ -109,203 +103,37 @@ procedure TSearchForm.FormShow(Sender: TObject); begin BtnSearch.Caption := 'Search'; BtnSearchBack.Visible := false; - FFoundCell := nil; + + FFoundCol := UNASSIGNED_ROW_COL_INDEX; + FFoundRow := UNASSIGNED_ROW_COL_INDEX; + FFoundWorksheet := nil; end; -procedure TSearchForm.Execute(AWorkbook: TsWorkbook; - var ASearchParams: TsSearchParams); +function TSearchForm.GetParams: TsSearchParams; begin - FWorkbook := AWorkbook; - ParamsToCtrls(ASearchParams); - Show; - CtrlsToParams(ASearchParams); -end; - -function TSearchForm.FindStartCell(AParams: TsSearchParams; - var AWorksheet: TsWorksheet; var AStartRow, AStartCol: Cardinal): Boolean; -var - sheetIndex: integer; - cell: PCell; -begin - Result := false; - cell := nil; - - // Case (1): Search not executed before - if FFoundCell = nil then - begin - case AParams.Start of - spsActiveCell: - begin - AWorksheet := FWorkbook.ActiveWorksheet; - AStartRow := AWorksheet.ActiveCellRow; - AStartCol := AWorksheet.ActiveCellCol; - end; - spsBeginningEnd: - if (soBackward in AParams.Options) then - begin - AWorksheet := FWorkbook.GetWorksheetByIndex(FWorkbook.GetWorksheetCount-1); - AStartCol := AWorksheet.GetLastColIndex; - AStartRow := AWorksheet.GetlastRowIndex; - end else - begin - AWorksheet := FWorkbook.GetWorksheetByIndex(0); - AStartCol := AWorksheet.GetFirstColIndex; - AStartRow := AWorksheet.GetFirstRowIndex; - end; - end; - end else - // Case (2): - // Repeated execution of search to start at cell adjacent to the one found in - // previous call. - begin - //AWorksheet := TsWorksheet(FFoundCell^.Worksheet); - // FoundCell is the cell found in the previous call. - //AStartRow := FFoundCell^.Row; - //AStartCol := FFoundCell^.Col; - sheetIndex := FWorkbook.GetWorksheetIndex(AWorksheet); - // Case (1): Find prior occupied cell along row - if (AParams.Options * [soAlongRows, soBackward] = [soAlongRows, soBackward]) then - begin - cell := AWorksheet.FindPrevCellInRow(AStartRow, AStartCol); - // No "prior" cell found in this row --> Proceed with previous row - while (cell = nil) and (AStartRow > 0) do - begin - dec(AStartRow); - AStartCol := AWorksheet.GetLastColIndex; - cell := AWorksheet.FindCell(AStartRow, AStartCol); - if (cell = nil) then - cell := AWorksheet.FindPrevCellInRow(AStartRow, AStartCol); - // No "prior" cell found in this sheet --> Proceed with previous sheet - if (cell = nil) and (AStartRow = 0) then - begin - if sheetIndex = 0 then - exit; - dec(sheetIndex); - AWorksheet := FWorkbook.GetWorksheetByIndex(sheetIndex); - AStartCol := AWorksheet.GetLastColIndex; - AStartRow := AWorksheet.GetLastRowIndex; - cell := AWorksheet.FindCell(AStartRow, AStartCol); - if (cell = nil) then - cell := AWorksheet.FindPrevCellInRow(AStartRow, AStartCol); - end; - end; - end - else - // Case (2): Find prior occupied cell along columns - if (AParams.Options * [soAlongRows, soBackward] = [soBackward]) then - begin - cell := AWorksheet.FindPrevCellInCol(AStartRow, AStartCol); - // No "preior" cell found in this column --> Proceed with previous column - while (cell = nil) and (AStartCol > 0) do - begin - dec(AStartCol); - AStartRow := AWorksheet.GetLastRowIndex; - cell := AWorksheet.FindCell(AStartRow, AStartCol); - if (cell = nil) then - cell := AWorksheet.FindPrevCellInCol(AStartRow, AStartCol); - // No "prior" cell found in this sheet --> Proceed with previous sheet - if (cell = nil) and (AStartCol = 0) then - begin - if sheetIndex = 0 then - exit; - dec(sheetIndex); - AWorksheet := FWorkbook.GetWorksheetByIndex(sheetIndex); - AStartCol := AWorksheet.GetLastColIndex; - AStartRow := AWorksheet.GetLastRowIndex; - cell := AWorksheet.FindCell(AStartRow, AStartCol); - if (cell = nil) then - cell := AWorksheet.FindPrevCellinCol(AStartRow, AStartCol); - end; - end; - end - else - // Case (3): Find next occupied cell along row - if (AParams.Options * [soAlongRows, soBackward] = [soAlongRows]) then - begin - cell := AWorksheet.FindNextCellInRow(AStartRow, AStartCol); - // No cell found in this row --> Proceed with next row - while (cell = nil) and (AStartRow < AWorksheet.GetLastRowIndex) do - begin - inc(AStartRow); - AStartCol := AWorksheet.GetFirstColIndex; - cell := AWorksheet.FindCell(AStartRow, AStartCol); - if (cell = nil) then - cell := AWorksheet.FindNextCellInRow(AStartRow, AStartCol); - // No "next" cell found in this sheet --> Proceed with next sheet - if (cell = nil) and (AStartRow = AWorksheet.GetLastRowIndex) then - begin - if sheetIndex = 0 then - exit; - inc(sheetIndex); - AWorksheet := FWorkbook.GetWorksheetByIndex(sheetIndex); - AStartCol := AWorksheet.GetLastColIndex; - AStartRow := AWorksheet.GetLastRowIndex; - cell := AWorksheet.FindCell(AStartRow, AStartCol); - if (cell = nil) then - cell := AWorksheet.FindNextCellInRow(AStartRow, AStartCol); - end; - end; - end - else - // Case (4): Find next occupied cell along column - if (AParams.Options * [soAlongRows, soBackward] = []) then - begin - cell := AWorksheet.FindNextCellInCol(AStartRow, AStartCol); - // No "next" occupied cell found in this column --> Proceed with next column - while (cell = nil) and (AStartCol < AWorksheet.GetLastColIndex) do - begin - inc(AStartCol); - AStartRow := AWorksheet.GetFirstRowIndex; - cell := AWorksheet.FindCell(AStartRow, AStartCol); - if (cell = nil) then - cell := AWorksheet.FindNextCellInCol(AStartRow, AStartCol); - // No "next" cell found in this sheet --> Proceed with next sheet - if (cell = nil) and (AStartCol = 0) then - begin - if sheetIndex = 0 then - exit; - inc(sheetIndex); - AWorksheet := FWorkbook.GetWorksheetByIndex(sheetIndex); - AStartCol := AWorksheet.GetLastColIndex; - AStartRow := AWorksheet.GetLastRowIndex; - cell := AWorksheet.FindCell(AStartRow, AStartCol); - if (cell = nil) then - cell := AWorksheet.FindNextCellInCol(AStartRow, AStartCol); - end; - end; - end; - end; - if cell <> nil then - begin - AStartRow := cell^.Row; - AStartCol := cell^.Col; - end; - Result := true; -end; - -procedure TSearchForm.ParamsToCtrls(const ASearchParams: TsSearchParams); -var - i: Integer; - o: TsSearchOption; -begin - CbSearchText.Text := ASearchParams.SearchText; - for o in TsSearchOption do - if ord(o) < CgSearchOptions.Items.Count then - CgSearchOptions.Checked[ord(o)] := (o in ASearchParams.Options); - RgSearchSource.ItemIndex := ord(ASearchParams.Source); - RgSearchStart.ItemIndex := ord(ASearchParams.Start); + Result.SearchText := CbSearchText.Text; + Result.Options := []; + if CgSearchOptions.Checked[COMPARE_ENTIRE_CELL] then + Include(Result.Options, soCompareEntireCell); + if CgSearchOptions.Checked[MATCH_CASE] then + Include(Result.Options, soMatchCase); + if CgSearchOptions.Checked[REGULAR_EXPRESSION] then + Include(Result.Options, soRegularExpr); + if CgSearchOptions.Checked[SEARCH_ALONG_ROWS] then + Include(Result.Options, soAlongRows); + if CgSearchOptions.Checked[CONTINUE_AT_START_END] then + Include(Result.Options, soWrapDocument); + if RgSearchStart.ItemIndex = 1 then + Include(Result.Options, soEntireDocument); + Result.Within := TsSearchWithin(RgSearchWithin.ItemIndex); end; procedure TSearchForm.SearchButtonClick(Sender: TObject); var - startsheet: TsWorksheet; - sheetIdx: Integer; - r,c: Cardinal; - backward: Boolean; params: TsSearchParams; - cell: PCell; + found: Boolean; begin - CtrlsToParams(params); + params := GetParams; if params.SearchText = '' then exit; @@ -316,66 +144,43 @@ begin CbSearchText.Items.Delete(CbSearchText.Items.Count-1); end; - if FFoundcell = nil then - backward := (soBackward in params.Options) // 1st call: use value from Options - else - backward := (Sender = BtnSearchBack); // subseq call: follow button - if backward then Include(params.Options, soBackward) else - Exclude(params.Options, soBackward); - - if params.Start = spsActiveCell then + if FSearchEngine = nil then begin - startSheet := FWorkbook.ActiveWorksheet; - FFoundCell := startSheet.FindCell(startSheet.ActiveCellRow, startSheet.ActiveCellCol); + FSearchEngine := TsSearchEngine.Create(FWorkbook); + if (soBackward in params.Options) then + Include(params.Options, soBackward) else + Exclude(params.Options, soBackward); + found := FSearchEngine.FindFirst(params.SearchText, params, FFoundWorksheet, FFoundRow, FFoundCol); + end else + begin + if (Sender = BtnSearchBack) then + Include(params.Options, soBackward) else + Exclude(params.Options, soBackward); + // User may select a different worksheet/different cell to continue search! + FFoundWorksheet := FWorkbook.ActiveWorksheet; + FFoundRow := FFoundWorksheet.ActiveCellRow; + FFoundCol := FFoundWorksheet.ActiveCellCol; + found := FSearchEngine.FindNext(params.SearchText, params, FFoundWorksheet, FFoundRow, FFoundCol); end; - if FFoundCell <> nil then - begin - startsheet := TsWorksheet(FFoundCell^.Worksheet); - r := FFoundCell^.Row; - c := FFoundCell^.Col; - end; - cell := nil; - - while FindStartCell(params, startsheet, r, c) and (cell = nil) do - begin - cell := startsheet.Search(params.SearchText, params.Options, r, c); - if (cell <> nil) then - begin - FWorkbook.SelectWorksheet(startsheet); - startsheet.SelectCell(cell^.Row, cell^.Col); - if Assigned(FOnFound) then FOnFound(Self, cell); - FFoundCell := cell; - break; - end; - // not found --> go to next sheet - case params.Source of - spsWorksheet: - break; - spsWorkbook: - begin - sheetIdx := FWorkbook.GetWorksheetIndex(startsheet); - if backward then - begin - if (sheetIdx = 0) then exit; - startsheet := FWorkbook.GetWorksheetByIndex(sheetIdx-1); - r := startsheet.GetLastRowIndex; - c := startsheet.GetLastColIndex; - end else - begin - if (sheetIdx = FWorkbook.GetWorksheetCount-1) then exit; - startsheet := FWorkbook.GetWorksheetByIndex(sheetIdx+1); - r := 0; - c := 0; - end; - end; - end; - end; + if Assigned(FOnFound) then + FOnFound(self, found, FFoundWorksheet, FFoundRow, FFoundCol); BtnSearchBack.Visible := true; BtnSearch.Caption := 'Next'; end; +procedure TSearchForm.SetParams(const AValue: TsSearchParams); +begin + CbSearchText.Text := Avalue.SearchText; + CgSearchOptions.Checked[COMPARE_ENTIRE_CELL] := (soCompareEntireCell in AValue.Options); + CgSearchOptions.Checked[MATCH_CASE] := (soMatchCase in AValue.Options); + CgSearchOptions.Checked[REGULAR_EXPRESSION] := (soRegularExpr in Avalue.Options); + CgSearchOptions.Checked[SEARCH_ALONG_ROWS] := (soAlongRows in AValue.Options); + CgSearchOptions.Checked[CONTINUE_AT_START_END] := (soWrapDocument in Avalue.Options); + RgSearchWithin.ItemIndex := ord(AValue.Within); + RgSearchStart.ItemIndex := ord(soEntireDocument in AValue.Options); +end; end. diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 6cd7cc451..5244f599a 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -450,11 +450,6 @@ type function GetSelectionCount: Integer; procedure SetSelection(const ASelection: TsCellRangeArray); - // Searching - function Search(ASearchText: String; AOptions: TsSearchOptions; - AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX; - AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell; - // Comments function FindComment(ACell: PCell): PsComment; function HasComment(ACell: PCell): Boolean; @@ -617,6 +612,7 @@ type FFileName: String; FLockCount: Integer; FLog: TStringList; + FSearchEngine: TObject; { Setter/Getter } function GetErrorMsg: String; @@ -733,12 +729,13 @@ type ABigEndian: Boolean = false); function UsesColor(AColorIndex: TsColor): Boolean; *) - + (* { Searching } - function Search(ASearchText: String; AOptions: TsSearchOptions; - AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX; - AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell; - + function SearchFirst(ASearchText: String; AParams: TsSearchParams; + out AWorksheet: TsWorksheet; out ARow, ACol: Cardinal): Boolean; + function SearchNext(out AWorksheet: TsWorksheet; + out ARow, ACol: Cardinal): Boolean; + *) { Utilities } procedure UpdateCaches; @@ -834,7 +831,7 @@ procedure CopyCellFormat(AFromCell, AToCell: PCell); implementation uses - Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser, RegExpr, + Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser, fpsStrings, uvirtuallayer_ole, fpsUtils, fpsHTMLUtils, fpsreaderwriter, fpsCurrency, fpsExprParser, fpsNumFormatParser; @@ -3594,186 +3591,6 @@ 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 = UNASSIGNED_ROW_COL_INDEX; - AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): 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 := ReadAsText(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 = UNASSIGNED_ROW_COL_INDEX then AStartRow := lastR; - if AStartCol = UNASSIGNED_ROW_COL_INDEX then AStartCol := lastC; - end else - begin - if AStartRow = UNASSIGNED_ROW_COL_INDEX then AStartRow := firstR; - if AStartCol = UNASSIGNED_ROW_COL_INDEX 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 -------------------------------------------------------------------------------} @@ -6513,66 +6330,37 @@ begin Unused(arg); TsWorksheet(data).Free; end; - + (* {@@ ---------------------------------------------------------------------------- - Searches the entire workbook for the first cell (after AStartCell) containing - a specified text. + Searches the first cell matching the ASearchText according to the + specified AParams. + Use SearchNext for subsequent calls for the next occurances. + The function result is TRUE if the search text has been found. In this case + AWorksheet, ARow and ACol specify the cell containing the search text. -------------------------------------------------------------------------------} -function TsWorkbook.Search(ASearchText: String; AOptions: TsSearchOptions; - AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX; - AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell; -var - i, idxSheet: Integer; - sheet: TsWorksheet; +function TsWorkbook.SearchFirst(ASearchText: String; AParams: TsSearchParams; + out AWorksheet: TsWorksheet; out ARow, ACol: Cardinal): Boolean; begin - // Setup missing default parameters - if soBackward in AOptions then - begin - if (AStartRow = UNASSIGNED_ROW_COL_INDEX) and (AStartCol = UNASSIGNED_ROW_COL_INDEX) and (AStartSheet = nil) - then AStartsheet := GetWorksheetByIndex(GetWorksheetCount-1); - if AStartRow = UNASSIGNED_ROW_COL_INDEX then - AStartRow := AStartsheet.GetLastRowIndex; - if AStartCol = UNASSIGNED_ROW_COL_INDEX then - AStartCol := AStartsheet.GetLastColIndex; - end else - begin - if (AStartRow = UNASSIGNED_ROW_COL_INDEX) and (AStartCol = UNASSIGNED_ROW_COL_INDEX) and (AStartSheet = nil) - then AStartsheet := GetWorksheetByIndex(0); - if (AStartRow = UNASSIGNED_ROW_COL_INDEX) then - AStartRow := AStartsheet.GetFirstRowIndex; - if (AStartCol = UNASSIGNED_ROW_COL_INDEX) 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; + FreeAndNil(FSearchEngine); + FSearchEngine := TsSearchEngine.Create(self); + with (FSearchEngine as TsSearchEngine) do + Result := FindFirst(ASearchText, AParams, AWorksheet, ARow, ACol); end; +{@@ ---------------------------------------------------------------------------- + Searches the next cell matching the text and params specified a the preceding + call to SearchFirst. + The function result is TRUE if the search text has been found. In this case + AWorksheet, ARow and ACol specify the cell containing the search text. +-------------------------------------------------------------------------------} +function TsWorkbook.SearchNext(out AWorksheet: TsWorksheet; + out ARow, ACol: Cardinal): Boolean; +begin + if FSearchEngine = nil then + Result := false else + Result := (FSearchEngine as TsSearchEngine).FindNext(AWorksheet, ARow, ACol); +end; + *) {@@ ---------------------------------------------------------------------------- Helper method to update internal caching variables -------------------------------------------------------------------------------} @@ -6628,6 +6416,8 @@ begin FFontList.Free; FLog.Free; + FreeAndNil(FSearchEngine); + inherited Destroy; end; diff --git a/components/fpspreadsheet/fpssearch.pas b/components/fpspreadsheet/fpssearch.pas new file mode 100644 index 000000000..96043b66d --- /dev/null +++ b/components/fpspreadsheet/fpssearch.pas @@ -0,0 +1,435 @@ +unit fpsSearch; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, RegExpr, fpstypes, fpspreadsheet; + +type + TsSearchEngine = class + private + FWorkbook: TsWorkbook; + FSearchText: String; + FParams: TsSearchParams; + FCurrSel: Integer; + FRegEx: TRegExpr; + protected + function ExecSearch(var AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; + procedure GotoFirst(out AWorksheet: TsWorksheet; out ARow, ACol: Cardinal); + procedure GotoLast(out AWorksheet: TsWorksheet; out ARow, ACol: Cardinal); + function GotoNext(var AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; + function GotoNextInWorksheet(AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; + function GotoPrev(var AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; + function GotoPrevInWorksheet(AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; + function Matches(AWorksheet: TsWorksheet; ARow, ACol: Cardinal): Boolean; + procedure PrepareSearchText(const ASearchText: String); + + public + constructor Create(AWorkbook: TsWorkbook); + destructor Destroy; override; + function FindFirst(const ASearchText: String; const AParams: TsSearchParams; + out AWorksheet: TsWorksheet; out ARow, ACol: Cardinal): Boolean; + function FindNext(const ASearchText: String; const AParams: TsSearchParams; + var AWorksheet: TsWorksheet; var ARow, ACol: Cardinal): Boolean; + end; + +implementation + +uses + lazutf8; + +constructor TsSearchEngine.Create(AWorkbook: TsWorkbook); +begin + inherited Create; + FWorkbook := AWorkbook; +end; + +destructor TsSearchEngine.Destroy; +begin + FreeAndNil(FRegEx); + inherited Destroy; +end; + +function TsSearchEngine.ExecSearch(var AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; +var + complete: boolean; + r, c: LongInt; + sheet: TsWorksheet; +begin + sheet := AWorksheet; + r := ARow; + c := ACol; + complete := false; + while (not complete) and (not Matches(AWorksheet, ARow, ACol)) do + begin + if soBackward in FParams.Options then + complete := not GotoPrev(AWorkSheet, ARow, ACol) else + complete := not GotoNext(AWorkSheet, ARow, ACol); + // Avoid infinite loop if search phrase does not exist in document. + if (AWorksheet = sheet) and (ARow = r) and (ACol = c) then + complete := true; + end; + Result := not complete; + if Result then + begin + FWorkbook.SelectWorksheet(AWorksheet); + AWorksheet.SelectCell(ARow, ACol); + end else + begin + AWorksheet := nil; + ARow := UNASSIGNED_ROW_COL_INDEX; + ACol := UNASSIGNED_ROW_COL_INDEX; + end; +end; + +function TsSearchEngine.FindFirst(const ASearchText: String; + const AParams: TsSearchParams; out AWorksheet: TsWorksheet; + out ARow, ACol: Cardinal): Boolean; +begin + FParams := AParams; + PrepareSearchText(ASearchText); + + if soBackward in FParams.Options then + GotoLast(AWorksheet, ARow, ACol) else + GotoFirst(AWorksheet, ARow, ACol); + + Result := ExecSearch(AWorksheet, ARow, ACol); +end; + +function TsSearchEngine.FindNext(const ASearchText: String; + const AParams: TsSearchParams; var AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; +begin + FParams := AParams; + PrepareSearchText(ASearchText); + + if soBackward in FParams.Options then + GotoPrev(AWorksheet, ARow, ACol) else + GotoNext(AWorksheet, ARow, ACol); + + Result := ExecSearch(AWorksheet, ARow, ACol); +end; + +procedure TsSearchEngine.GotoFirst(out AWorksheet: TsWorksheet; + out ARow, ACol: Cardinal); +begin + if soEntireDocument in FParams.Options then + // Search entire document forward from start + case FParams.Within of + swWorkbook : + begin + AWorksheet := FWorkbook.GetWorksheetByIndex(0); + ARow := 0; + ACol := 0; + end; + swWorksheet: + begin + AWorksheet := FWorkbook.ActiveWorksheet; + ARow := 0; + ACol := 0; + end; + swColumn: + begin + AWorksheet := FWorkbook.ActiveWorksheet; + ARow := 0; + ACol := AWorksheet.ActiveCellCol; + end; + swRow: + begin + AWorksheet := FWorkbook.ActiveWorksheet; + ARow := AWorksheet.ActiveCellRow; + ACol := 0; + end; + end + else + begin + // Search starts at active cell + AWorksheet := FWorkbook.ActiveWorksheet; + ARow := AWorksheet.ActiveCellRow; + ACol := AWorksheet.ActiveCellCol; + end; +end; + +procedure TsSearchEngine.GotoLast(out AWorksheet: TsWorksheet; + out ARow, ACol: Cardinal); +var + cell: PCell; + sel: TsCellRangeArray; +begin + if soEntireDocument in FParams.Options then + // Search entire document backward from end + case FParams.Within of + swWorkbook : + begin + AWorksheet := FWorkbook.GetWorksheetByIndex(FWorkbook.GetWorksheetCount-1); + ARow := AWorksheet.GetLastRowIndex; + ACol := AWorksheet.GetLastColIndex; + end; + swWorksheet: + begin + AWorksheet := FWorkbook.ActiveWorksheet; + ARow := AWorksheet.GetLastRowIndex; + ACol := AWorksheet.GetLastColIndex; + end; + swColumn: + begin + AWorksheet := FWorkbook.ActiveWorksheet; + ARow := AWorksheet.GetLastRowIndex; + ACol := AWorksheet.ActiveCellCol; + end; + swRow: + begin + AWorksheet := FWorkbook.ActiveWorksheet; + ARow := AWorksheet.ActiveCellRow; + ACol := AWorksheet.GetLastColIndex; + end; + end + else + begin + // Search starts at active cell + AWorksheet := FWorkbook.ActiveWorksheet; + ARow := AWorksheet.ActiveCellRow; + ACol := AWorksheet.ActiveCellCol; + end; +end; + +function TsSearchEngine.GotoNext(var AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; +var + idx: Integer; + sel: TsCellRangeArray; +begin + Result := true; + + if GotoNextInWorksheet(AWorksheet, ARow, ACol) then + exit; + + case FParams.Within of + swWorkbook: + begin + // Need to go to next sheet + idx := FWorkbook.GetWorksheetIndex(AWorksheet) + 1; + if idx < FWorkbook.GetWorksheetCount then + begin + AWorksheet := FWorkbook.GetWorksheetByIndex(idx); + ARow := 0; + ACol := 0; + exit; + end; + // Continue search with first worksheet + if (soWrapDocument in FParams.Options) then + begin + AWorksheet := FWorkbook.GetWorksheetByIndex(0); + ARow := 0; + ACol := 0; + exit; + end; + end; + + swWorksheet: + if soWrapDocument in FParams.Options then begin + ARow := 0; + ACol := 0; + exit; + end; + + swColumn: + if soWrapDocument in FParams.Options then begin + ARow := 0; + ACol := AWorksheet.ActiveCellCol; + exit; + end; + + swRow: + if soWrapDocument in FParams.Options then begin + ARow := AWorksheet.ActiveCellRow; + ACol := 0; + exit; + end; + end; // case + + Result := false; +end; + + +function TsSearchEngine.GotoNextInWorksheet(AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; +begin + Result := true; + if (soAlongRows in FParams.Options) or (FParams.Within = swRow) then + begin + inc(ACol); + if ACol <= AWorksheet.GetLastColIndex then + exit; + if (FParams.Within <> swRow) then + begin + ACol := 0; + inc(ARow); + if ARow <= AWorksheet.GetLastRowIndex then + exit; + end; + end else + if not (soAlongRows in FParams.Options) or (FParams.Within = swColumn) then + begin + inc(ARow); + if ARow <= AWorksheet.GetLastRowIndex then + exit; + if (FParams.Within <> swColumn) then + begin + ARow := 0; + inc(ACol); + if (ACol <= AWorksheet.GetLastColIndex) then + exit; + end; + end; + // We reached the last cell, there is no "next" cell in this sheet + Result := false; +end; + +function TsSearchEngine.GotoPrev(var AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; +var + idx: Integer; + sel: TsCellRangeArray; +begin + Result := true; + + if GotoPrevInWorksheet(AWorksheet, ARow, ACol) then + exit; + + case FParams.Within of + swWorkbook: + begin + // Need to go to previous sheet + idx := FWorkbook.GetWorksheetIndex(AWorksheet) - 1; + if idx >= 0 then + begin + AWorksheet := FWorkbook.GetWorksheetByIndex(idx); + ARow := AWorksheet.GetLastRowIndex; + ACol := AWorksheet.GetlastColIndex; + exit; + end; + if (soWrapDocument in FParams.Options) then + begin + AWorksheet := FWorkbook.GetWorksheetByIndex(FWorkbook.GetWorksheetCount-1); + ARow := AWorksheet.GetLastRowIndex; + ACol := AWorksheet.GetLastColIndex; + exit; + end; + end; + + swWorksheet: + if soWrapDocument in FParams.Options then + begin + ARow := AWorksheet.GetLastRowIndex; + ACol := AWorksheet.GetLastColIndex; + exit; + end; + + swColumn: + if soWrapDocument in FParams.Options then + begin + ARow := AWorksheet.GetLastRowIndex; + ACol := AWorksheet.ActiveCellCol; + exit; + end; + + swRow: + if soWrapDocument in FParams.Options then + begin + ARow := AWorksheet.ActiveCellRow; + ACol := AWorksheet.GetLastColIndex; + exit; + end; + end; // case + + Result := false; +end; + +function TsSearchEngine.GotoPrevInWorksheet(AWorksheet: TsWorksheet; + var ARow, ACol: Cardinal): Boolean; +begin + Result := true; + if (soAlongRows in FParams.Options) or (FParams.Within = swRow) then + begin + if ACol > 0 then begin + dec(ACol); + exit; + end; + if (FParams.Within <> swRow) then + begin + ACol := AWorksheet.GetLastColIndex; + if ARow > 0 then + begin + dec(ARow); + exit; + end; + end; + end else + if not (soAlongRows in FParams.Options) or (FParams.Within = swColumn) then + begin + if ARow > 0 then begin + dec(ARow); + exit; + end; + if (FParams.Within <> swColumn) then + begin + ARow := AWorksheet.GetlastRowIndex; + if ACol > 0 then + begin + dec(ACol); + exit; + end; + end; + end; + // We reached the first cell, there is no "previous" cell + Result := false; +end; + +function TsSearchEngine.Matches(AWorksheet: TsWorksheet; ARow, ACol: Cardinal): Boolean; +var + cell: PCell; + celltxt: String; +begin + cell := AWorksheet.FindCell(ARow, ACol); + if cell <> nil then + celltxt := AWorksheet.ReadAsText(cell) else + celltxt := ''; + + if soRegularExpr in FParams.Options then + Result := FRegEx.Exec(celltxt) + else + begin + if not (soMatchCase in FParams.Options) then + celltxt := UTF8Lowercase(celltxt); + if soCompareEntireCell in FParams.Options then + exit(celltxt = FSearchText); + if UTF8Pos(FSearchText, celltxt) > 0 then + exit(true); + Result := false; + end; +end; + +procedure TsSearchEngine.PrepareSearchText(const ASearchText: String); +begin + if soRegularExpr in FParams.Options then + begin + FreeAndNil(FRegEx); + FRegEx := TRegExpr.Create; + FRegEx.Expression := ASearchText + end else + if (soMatchCase in FParams.Options) then + FSearchText := ASearchText else + FSearchText := UTF8Lowercase(ASearchText); +end; + +end. + diff --git a/components/fpspreadsheet/fpstypes.pas b/components/fpspreadsheet/fpstypes.pas index e4cc372ed..4891cc065 100644 --- a/components/fpspreadsheet/fpstypes.pas +++ b/components/fpspreadsheet/fpstypes.pas @@ -698,12 +698,6 @@ 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; @@ -712,6 +706,25 @@ const HEADER_FOOTER_INDEX_ALL = 1; +type + {@@ Search option } + TsSearchOption = (soCompareEntireCell, soMatchCase, soRegularExpr, soAlongRows, + soBackward, soWrapDocument, soEntireDocument); + + {@@ A set of search options } + TsSearchOptions = set of TsSearchOption; + + {@@ Defines which part of document is scanned } + TsSearchWithin = (swWorkbook, swWorksheet, swColumn, swRow); + + {@@ Search parameters } + TsSearchParams = record + SearchText: String; + Options: TsSearchOptions; + Within: TsSearchWithin; + end; + + implementation constructor TsFont.Create(AFontName: String; ASize: Single; AStyle: TsFontStyles; diff --git a/components/fpspreadsheet/laz_fpspreadsheet.lpk b/components/fpspreadsheet/laz_fpspreadsheet.lpk index c3b508e79..548200b98 100644 --- a/components/fpspreadsheet/laz_fpspreadsheet.lpk +++ b/components/fpspreadsheet/laz_fpspreadsheet.lpk @@ -30,7 +30,7 @@ This package is all you need if you don't want graphical components (like grids and charts)."/> - + @@ -183,6 +183,10 @@ This package is all you need if you don't want graphical components (like grids + + + + diff --git a/components/fpspreadsheet/laz_fpspreadsheet.pas b/components/fpspreadsheet/laz_fpspreadsheet.pas index f0e4dcee3..e2e52186c 100644 --- a/components/fpspreadsheet/laz_fpspreadsheet.pas +++ b/components/fpspreadsheet/laz_fpspreadsheet.pas @@ -14,7 +14,7 @@ uses fpolebasic, wikitable, fpsNumFormatParser, fpsfunc, fpsRPN, fpsStrings, fpscsv, fpsCsvDocument, fpspatches, fpsTypes, xlsEscher, fpsReaderWriter, fpsNumFormat, fpsclasses, fpsHeaderFooterParser, fpsPalette, fpsHTML, - fpsHTMLUtils, fpsCell; + fpsHTMLUtils, fpsCell, fpsSearch; implementation