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