You've already forked lazarus-ccr
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:
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
435
components/fpspreadsheet/fpssearch.pas
Normal file
435
components/fpspreadsheet/fpssearch.pas
Normal 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.
|
||||
|
@ -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;
|
||||
|
@ -30,7 +30,7 @@
|
||||
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)."/>
|
||||
<Version Major="1" Minor="7"/>
|
||||
<Files Count="38">
|
||||
<Files Count="39">
|
||||
<Item1>
|
||||
<Filename Value="fpolestorage.pas"/>
|
||||
<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"/>
|
||||
<UnitName Value="fpsCell"/>
|
||||
</Item38>
|
||||
<Item39>
|
||||
<Filename Value="fpssearch.pas"/>
|
||||
<UnitName Value="fpsSearch"/>
|
||||
</Item39>
|
||||
</Files>
|
||||
<RequiredPkgs Count="2">
|
||||
<Item1>
|
||||
|
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user