fpspreadsheet: Redo searching (better OO code), some identifiers renamed with respect to initial commit.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4313 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-09-07 15:48:43 +00:00
parent c8d8277863
commit 49dea52ee1
8 changed files with 650 additions and 572 deletions

View File

@ -355,7 +355,9 @@ type
const AHyperlink: TsHyperlink); const AHyperlink: TsHyperlink);
private private
{ private declarations } { 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; procedure UpdateCaption;
protected protected
procedure ReadFromIni; procedure ReadFromIni;
@ -496,7 +498,9 @@ begin
if SearchForm = nil then if SearchForm = nil then
SearchForm := TSearchForm.Create(self); SearchForm := TSearchForm.Create(self);
SearchForm.OnFound := @SearchFound; SearchForm.OnFound := @SearchFound;
SearchForm.Execute(WorkbookSource.Workbook, DefaultSearchParams); SearchForm.OnClose := @SearchClose;
SearchForm.SearchParams := DefaultSearchParams;
SearchForm.Execute(WorkbookSource.Workbook);
end; end;
procedure TMainForm.AcSettingsCSVParamsExecute(Sender: TObject); procedure TMainForm.AcSettingsCSVParamsExecute(Sender: TObject);
@ -601,9 +605,30 @@ begin
end; end;
end; end;
procedure TMainForm.SearchFound(Sender: TObject; ACell: PCell); procedure TMainForm.SearchClose(Sender: TObject; var CloseAction: TCloseAction);
begin 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; end;
procedure TMainForm.UpdateCaption; procedure TMainForm.UpdateCaption;

View File

@ -1,12 +1,12 @@
object SearchForm: TSearchForm object SearchForm: TSearchForm
Left = 238 Left = 238
Height = 271 Height = 272
Top = 157 Top = 157
Width = 392 Width = 483
BorderStyle = bsDialog BorderStyle = bsDialog
Caption = 'Search' Caption = 'Search'
ClientHeight = 271 ClientHeight = 272
ClientWidth = 392 ClientWidth = 483
FormStyle = fsStayOnTop FormStyle = fsStayOnTop
OnClose = FormClose OnClose = FormClose
OnCreate = FormCreate OnCreate = FormCreate
@ -24,16 +24,16 @@ object SearchForm: TSearchForm
Left = 93 Left = 93
Height = 23 Height = 23
Top = 14 Top = 14
Width = 283 Width = 374
Anchors = [akTop, akLeft, akRight] Anchors = [akTop, akLeft, akRight]
ItemHeight = 15 ItemHeight = 15
TabOrder = 0 TabOrder = 0
end end
object CgSearchOptions: TCheckGroup object CgSearchOptions: TCheckGroup
Left = 19 Left = 16
Height = 163 Height = 163
Top = 53 Top = 53
Width = 189 Width = 192
AutoFill = True AutoFill = True
Caption = 'Search options' Caption = 'Search options'
ChildSizing.LeftRightSpacing = 6 ChildSizing.LeftRightSpacing = 6
@ -45,24 +45,24 @@ object SearchForm: TSearchForm
ChildSizing.Layout = cclLeftToRightThenTopToBottom ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 1 ChildSizing.ControlsPerLine = 1
ClientHeight = 143 ClientHeight = 143
ClientWidth = 185 ClientWidth = 188
Items.Strings = ( Items.Strings = (
'Compare full cell ' 'Compare entire cell '
'Ignore case' 'Match case'
'Regular expression' 'Regular expression'
'Backwards'
'Search along rows' 'Search along rows'
'Continue at start/end'
) )
TabOrder = 1 TabOrder = 1
Data = { Data = {
050000000202020202 050000000202020202
} }
end end
object RgSearchSource: TRadioGroup object RgSearchWithin: TRadioGroup
Left = 237 Left = 232
Height = 75 Height = 67
Top = 53 Top = 53
Width = 139 Width = 232
AutoFill = True AutoFill = True
Caption = 'Search within' Caption = 'Search within'
ChildSizing.LeftRightSpacing = 6 ChildSizing.LeftRightSpacing = 6
@ -70,39 +70,43 @@ object SearchForm: TSearchForm
ChildSizing.EnlargeVertical = crsHomogenousChildResize ChildSizing.EnlargeVertical = crsHomogenousChildResize
ChildSizing.ShrinkHorizontal = crsScaleChilds ChildSizing.ShrinkHorizontal = crsScaleChilds
ChildSizing.ShrinkVertical = crsScaleChilds ChildSizing.ShrinkVertical = crsScaleChilds
ChildSizing.Layout = cclLeftToRightThenTopToBottom ChildSizing.Layout = cclTopToBottomThenLeftToRight
ChildSizing.ControlsPerLine = 1 ChildSizing.ControlsPerLine = 2
ClientHeight = 55 ClientHeight = 47
ClientWidth = 135 ClientWidth = 228
ColumnLayout = clVerticalThenHorizontal
Columns = 2
ItemIndex = 0 ItemIndex = 0
Items.Strings = ( Items.Strings = (
'worksheet'
'workbook' 'workbook'
'worksheet'
'column'
'row'
) )
TabOrder = 2 TabOrder = 2
end end
object ButtonPanel: TPanel object ButtonPanel: TPanel
Left = 0 Left = 0
Height = 38 Height = 38
Top = 233 Top = 234
Width = 392 Width = 483
Align = alBottom Align = alBottom
BevelOuter = bvNone BevelOuter = bvNone
ClientHeight = 38 ClientHeight = 38
ClientWidth = 392 ClientWidth = 483
TabOrder = 3 TabOrder = 3
object Bevel1: TBevel object Bevel1: TBevel
Left = 6 Left = 6
Height = 3 Height = 3
Top = 0 Top = 0
Width = 380 Width = 471
Align = alTop Align = alTop
BorderSpacing.Left = 6 BorderSpacing.Left = 6
BorderSpacing.Right = 6 BorderSpacing.Right = 6
Shape = bsTopLine Shape = bsTopLine
end end
object BtnSearchBack: TBitBtn object BtnSearchBack: TBitBtn
Left = 149 Left = 240
Height = 25 Height = 25
Top = 7 Top = 7
Width = 75 Width = 75
@ -149,18 +153,19 @@ object SearchForm: TSearchForm
Visible = False Visible = False
end end
object BtnClose: TBitBtn object BtnClose: TBitBtn
Left = 309 Left = 400
Height = 25 Height = 25
Top = 7 Top = 7
Width = 75 Width = 75
Anchors = [akTop, akRight] Anchors = [akTop, akRight]
Cancel = True
DefaultCaption = True DefaultCaption = True
Kind = bkClose Kind = bkClose
ModalResult = 11 ModalResult = 11
TabOrder = 1 TabOrder = 1
end end
object BtnSearch: TBitBtn object BtnSearch: TBitBtn
Left = 229 Left = 320
Height = 25 Height = 25
Top = 7 Top = 7
Width = 75 Width = 75
@ -208,10 +213,10 @@ object SearchForm: TSearchForm
end end
end end
object RgSearchStart: TRadioGroup object RgSearchStart: TRadioGroup
Left = 237 Left = 232
Height = 75 Height = 56
Top = 141 Top = 160
Width = 139 Width = 232
AutoFill = True AutoFill = True
Caption = 'Start search at' Caption = 'Start search at'
ChildSizing.LeftRightSpacing = 6 ChildSizing.LeftRightSpacing = 6
@ -220,13 +225,14 @@ object SearchForm: TSearchForm
ChildSizing.ShrinkHorizontal = crsScaleChilds ChildSizing.ShrinkHorizontal = crsScaleChilds
ChildSizing.ShrinkVertical = crsScaleChilds ChildSizing.ShrinkVertical = crsScaleChilds
ChildSizing.Layout = cclLeftToRightThenTopToBottom ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 1 ChildSizing.ControlsPerLine = 2
ClientHeight = 55 ClientHeight = 36
ClientWidth = 135 ClientWidth = 228
Columns = 2
ItemIndex = 0 ItemIndex = 0
Items.Strings = ( Items.Strings = (
'beginning/end'
'active cell' 'active cell'
'beginning/end'
) )
TabOrder = 4 TabOrder = 4
end end

View File

@ -5,25 +5,12 @@ unit sSearchForm;
interface interface
uses uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ButtonPanel, Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls, Buttons, fpsTypes, fpspreadsheet; StdCtrls, ExtCtrls, Buttons, fpsTypes, fpspreadsheet, fpsSearch;
type type
TsSearchEvent = procedure (Sender: TObject; AFound: Boolean;
{ TSearchParams } AWorksheet: TsWorksheet; ARow, ACol: Cardinal) of object;
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;
{ TSearchForm } { TSearchForm }
@ -37,24 +24,25 @@ type
LblSearchText: TLabel; LblSearchText: TLabel;
ButtonPanel: TPanel; ButtonPanel: TPanel;
RgSearchStart: TRadioGroup; RgSearchStart: TRadioGroup;
RgSearchSource: TRadioGroup; RgSearchWithin: TRadioGroup;
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject); procedure FormShow(Sender: TObject);
procedure SearchButtonClick(Sender: TObject); procedure SearchButtonClick(Sender: TObject);
private private
{ private declarations } { private declarations }
FSearchEngine: TsSearchEngine;
FWorkbook: TsWorkbook; FWorkbook: TsWorkbook;
FFoundCell: PCell; FFoundWorksheet: TsWorksheet;
FFoundRow, FFoundCol: Cardinal;
FOnFound: TsSearchEvent; FOnFound: TsSearchEvent;
procedure CtrlsToParams(var ASearchParams: TsSearchParams); function GetParams: TsSearchParams;
function FindStartCell(AParams: TsSearchParams; var AWorksheet: TsWorksheet; procedure SetParams(const AValue: TsSearchParams);
var AStartRow, AStartCol: Cardinal): Boolean;
procedure ParamsToCtrls(const ASearchParams: TsSearchParams);
public public
{ public declarations } { public declarations }
procedure Execute(AWorkbook: TsWorkbook; var ASearchParams: TsSearchParams); procedure Execute(AWorkbook: TsWorkbook);
property Workbook: TsWorkbook read FWorkbook; property Workbook: TsWorkbook read FWorkbook;
property SearchParams: TsSearchParams read GetParams write SetParams;
property OnFound: TsSearchEvent read FOnFound write FOnFound; property OnFound: TsSearchEvent read FOnFound write FOnFound;
end; end;
@ -63,9 +51,8 @@ var
DefaultSearchParams: TsSearchParams = ( DefaultSearchParams: TsSearchParams = (
SearchText: ''; SearchText: '';
Options: [soIgnoreCase]; Options: [];
Source: spsWorksheet; Within: swWorksheet
Start: spsActiveCell;
); );
@ -73,26 +60,33 @@ implementation
{$R *.lfm} {$R *.lfm}
const uses
MAX_SEARCH_ITEMS = 10; fpsUtils;
procedure TSearchForm.CtrlsToParams(var ASearchParams: TsSearchParams); const
var MAX_SEARCH_ITEMS = 10;
i: Integer;
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 begin
ASearchParams.SearchText := CbSearchText.Text; FWorkbook := AWorkbook;
ASearchParams.Options := []; Show;
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);
end; end;
procedure TSearchForm.FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure TSearchForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
var var
P: TPoint; P: TPoint;
begin begin
Unused(CloseAction);
FreeAndNil(FSearchEngine);
P.X := Left; P.X := Left;
P.Y := Top; P.Y := Top;
Position := poDesigned; Position := poDesigned;
@ -109,203 +103,37 @@ procedure TSearchForm.FormShow(Sender: TObject);
begin begin
BtnSearch.Caption := 'Search'; BtnSearch.Caption := 'Search';
BtnSearchBack.Visible := false; BtnSearchBack.Visible := false;
FFoundCell := nil;
FFoundCol := UNASSIGNED_ROW_COL_INDEX;
FFoundRow := UNASSIGNED_ROW_COL_INDEX;
FFoundWorksheet := nil;
end; end;
procedure TSearchForm.Execute(AWorkbook: TsWorkbook; function TSearchForm.GetParams: TsSearchParams;
var ASearchParams: TsSearchParams);
begin begin
FWorkbook := AWorkbook; Result.SearchText := CbSearchText.Text;
ParamsToCtrls(ASearchParams); Result.Options := [];
Show; if CgSearchOptions.Checked[COMPARE_ENTIRE_CELL] then
CtrlsToParams(ASearchParams); Include(Result.Options, soCompareEntireCell);
end; if CgSearchOptions.Checked[MATCH_CASE] then
Include(Result.Options, soMatchCase);
function TSearchForm.FindStartCell(AParams: TsSearchParams; if CgSearchOptions.Checked[REGULAR_EXPRESSION] then
var AWorksheet: TsWorksheet; var AStartRow, AStartCol: Cardinal): Boolean; Include(Result.Options, soRegularExpr);
var if CgSearchOptions.Checked[SEARCH_ALONG_ROWS] then
sheetIndex: integer; Include(Result.Options, soAlongRows);
cell: PCell; if CgSearchOptions.Checked[CONTINUE_AT_START_END] then
begin Include(Result.Options, soWrapDocument);
Result := false; if RgSearchStart.ItemIndex = 1 then
cell := nil; Include(Result.Options, soEntireDocument);
Result.Within := TsSearchWithin(RgSearchWithin.ItemIndex);
// 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);
end; end;
procedure TSearchForm.SearchButtonClick(Sender: TObject); procedure TSearchForm.SearchButtonClick(Sender: TObject);
var var
startsheet: TsWorksheet;
sheetIdx: Integer;
r,c: Cardinal;
backward: Boolean;
params: TsSearchParams; params: TsSearchParams;
cell: PCell; found: Boolean;
begin begin
CtrlsToParams(params); params := GetParams;
if params.SearchText = '' then if params.SearchText = '' then
exit; exit;
@ -316,66 +144,43 @@ begin
CbSearchText.Items.Delete(CbSearchText.Items.Count-1); CbSearchText.Items.Delete(CbSearchText.Items.Count-1);
end; end;
if FFoundcell = nil then if FSearchEngine = 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
begin begin
startSheet := FWorkbook.ActiveWorksheet; FSearchEngine := TsSearchEngine.Create(FWorkbook);
FFoundCell := startSheet.FindCell(startSheet.ActiveCellRow, startSheet.ActiveCellCol); 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; end;
if FFoundCell <> nil then if Assigned(FOnFound) then
begin FOnFound(self, found, FFoundWorksheet, FFoundRow, FFoundCol);
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;
BtnSearchBack.Visible := true; BtnSearchBack.Visible := true;
BtnSearch.Caption := 'Next'; BtnSearch.Caption := 'Next';
end; 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. end.

View File

@ -450,11 +450,6 @@ type
function GetSelectionCount: Integer; function GetSelectionCount: Integer;
procedure SetSelection(const ASelection: TsCellRangeArray); 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 // Comments
function FindComment(ACell: PCell): PsComment; function FindComment(ACell: PCell): PsComment;
function HasComment(ACell: PCell): Boolean; function HasComment(ACell: PCell): Boolean;
@ -617,6 +612,7 @@ type
FFileName: String; FFileName: String;
FLockCount: Integer; FLockCount: Integer;
FLog: TStringList; FLog: TStringList;
FSearchEngine: TObject;
{ Setter/Getter } { Setter/Getter }
function GetErrorMsg: String; function GetErrorMsg: String;
@ -733,12 +729,13 @@ type
ABigEndian: Boolean = false); ABigEndian: Boolean = false);
function UsesColor(AColorIndex: TsColor): Boolean; function UsesColor(AColorIndex: TsColor): Boolean;
*) *)
(*
{ Searching } { Searching }
function Search(ASearchText: String; AOptions: TsSearchOptions; function SearchFirst(ASearchText: String; AParams: TsSearchParams;
AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX; out AWorksheet: TsWorksheet; out ARow, ACol: Cardinal): Boolean;
AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell; function SearchNext(out AWorksheet: TsWorksheet;
out ARow, ACol: Cardinal): Boolean;
*)
{ Utilities } { Utilities }
procedure UpdateCaches; procedure UpdateCaches;
@ -834,7 +831,7 @@ procedure CopyCellFormat(AFromCell, AToCell: PCell);
implementation implementation
uses uses
Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser, RegExpr, Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser,
fpsStrings, uvirtuallayer_ole, fpsStrings, uvirtuallayer_ole,
fpsUtils, fpsHTMLUtils, fpsreaderwriter, fpsCurrency, fpsExprParser, fpsUtils, fpsHTMLUtils, fpsreaderwriter, fpsCurrency, fpsExprParser,
fpsNumFormatParser; fpsNumFormatParser;
@ -3594,186 +3591,6 @@ begin
FSelection[i] := ASelection[i]; FSelection[i] := ASelection[i];
end; 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 Helper method to update internal caching variables
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -6513,66 +6330,37 @@ begin
Unused(arg); Unused(arg);
TsWorksheet(data).Free; TsWorksheet(data).Free;
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Searches the entire workbook for the first cell (after AStartCell) containing Searches the first cell matching the ASearchText according to the
a specified text. 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; function TsWorkbook.SearchFirst(ASearchText: String; AParams: TsSearchParams;
AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = UNASSIGNED_ROW_COL_INDEX; out AWorksheet: TsWorksheet; out ARow, ACol: Cardinal): Boolean;
AStartCol: Cardinal = UNASSIGNED_ROW_COL_INDEX): PCell;
var
i, idxSheet: Integer;
sheet: TsWorksheet;
begin begin
// Setup missing default parameters FreeAndNil(FSearchEngine);
if soBackward in AOptions then FSearchEngine := TsSearchEngine.Create(self);
begin with (FSearchEngine as TsSearchEngine) do
if (AStartRow = UNASSIGNED_ROW_COL_INDEX) and (AStartCol = UNASSIGNED_ROW_COL_INDEX) and (AStartSheet = nil) Result := FindFirst(ASearchText, AParams, AWorksheet, ARow, ACol);
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;
end; 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 Helper method to update internal caching variables
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -6628,6 +6416,8 @@ begin
FFontList.Free; FFontList.Free;
FLog.Free; FLog.Free;
FreeAndNil(FSearchEngine);
inherited Destroy; inherited Destroy;
end; end;

View File

@ -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.

View File

@ -698,12 +698,6 @@ type
{@@ Pointer to a page layout record } {@@ Pointer to a page layout record }
PsPageLayout = ^TsPageLayout; PsPageLayout = ^TsPageLayout;
{@@ Search option }
TsSearchOption = (soCompareFullCell, soIgnoreCase, soRegularExpr,
soBackward, soAlongRows);
TsSearchOptions = set of TsSearchOption;
const const
{@@ Indexes to be used for the various headers and footers } {@@ Indexes to be used for the various headers and footers }
HEADER_FOOTER_INDEX_FIRST = 0; HEADER_FOOTER_INDEX_FIRST = 0;
@ -712,6 +706,25 @@ const
HEADER_FOOTER_INDEX_ALL = 1; 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 implementation
constructor TsFont.Create(AFontName: String; ASize: Single; AStyle: TsFontStyles; constructor TsFont.Create(AFontName: String; ASize: Single; AStyle: TsFontStyles;

View File

@ -30,7 +30,7 @@
This package is all you need if you don't want graphical components (like grids and charts)."/> This package is all you need if you don't want graphical components (like grids and charts)."/>
<License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/> <License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/>
<Version Major="1" Minor="7"/> <Version Major="1" Minor="7"/>
<Files Count="38"> <Files Count="39">
<Item1> <Item1>
<Filename Value="fpolestorage.pas"/> <Filename Value="fpolestorage.pas"/>
<UnitName Value="fpolestorage"/> <UnitName Value="fpolestorage"/>
@ -183,6 +183,10 @@ This package is all you need if you don't want graphical components (like grids
<Filename Value="fpscell.pas"/> <Filename Value="fpscell.pas"/>
<UnitName Value="fpsCell"/> <UnitName Value="fpsCell"/>
</Item38> </Item38>
<Item39>
<Filename Value="fpssearch.pas"/>
<UnitName Value="fpsSearch"/>
</Item39>
</Files> </Files>
<RequiredPkgs Count="2"> <RequiredPkgs Count="2">
<Item1> <Item1>

View File

@ -14,7 +14,7 @@ uses
fpolebasic, wikitable, fpsNumFormatParser, fpsfunc, fpsRPN, fpsStrings, fpolebasic, wikitable, fpsNumFormatParser, fpsfunc, fpsRPN, fpsStrings,
fpscsv, fpsCsvDocument, fpspatches, fpsTypes, xlsEscher, fpsReaderWriter, fpscsv, fpsCsvDocument, fpspatches, fpsTypes, xlsEscher, fpsReaderWriter,
fpsNumFormat, fpsclasses, fpsHeaderFooterParser, fpsPalette, fpsHTML, fpsNumFormat, fpsclasses, fpsHeaderFooterParser, fpsPalette, fpsHTML,
fpsHTMLUtils, fpsCell; fpsHTMLUtils, fpsCell, fpsSearch;
implementation implementation