fpspreadsheet: Initial version of searching in workbook/worksheet, not perfect yet.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4214 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2015-07-27 23:01:52 +00:00
parent 42f5ce9be2
commit ef69632cea
6 changed files with 473 additions and 40 deletions

View File

@ -60,7 +60,7 @@
<PackageName Value="LCL"/>
</Item2>
</RequiredPackages>
<Units Count="8">
<Units Count="9">
<Unit0>
<Filename Value="demo_ctrls.lpr"/>
<IsPartOfProject Value="True"/>
@ -117,6 +117,14 @@
<ResourceBaseClass Value="Form"/>
<UnitName Value="sNumFormatForm"/>
</Unit7>
<Unit8>
<Filename Value="..\shared\ssearchform.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="SearchForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="sSearchForm"/>
</Unit8>
</Units>
</ProjectOptions>
<CompilerOptions>

View File

@ -7,7 +7,7 @@ uses
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, main, sHyperlinkForm, sNumFormatForm;
Forms, main, sHyperlinkForm, sNumFormatForm, sSearchForm;
{$R *.res}

View File

@ -2,10 +2,10 @@ object MainForm: TMainForm
Left = 338
Height = 621
Top = 118
Width = 940
Width = 997
Caption = 'demo_ctrls'
ClientHeight = 601
ClientWidth = 940
ClientWidth = 997
Menu = MainMenu
ShowHint = True
LCLVersion = '1.5'
@ -13,7 +13,7 @@ object MainForm: TMainForm
Left = 0
Height = 518
Top = 83
Width = 654
Width = 711
TabIndex = 0
Tabs.Strings = (
'Sheet1'
@ -25,7 +25,7 @@ object MainForm: TMainForm
Left = 2
Height = 493
Top = 23
Width = 650
Width = 707
FrozenCols = 0
FrozenRows = 0
ReadFormulas = False
@ -50,7 +50,7 @@ object MainForm: TMainForm
end
end
object InspectorTabControl: TTabControl
Left = 659
Left = 716
Height = 518
Top = 83
Width = 281
@ -123,7 +123,7 @@ object MainForm: TMainForm
end
end
object InspectorSplitter: TSplitter
Left = 654
Left = 711
Height = 518
Top = 83
Width = 5
@ -135,7 +135,7 @@ object MainForm: TMainForm
Left = 0
Height = 26
Top = 24
Width = 940
Width = 997
AutoSize = True
ButtonHeight = 26
ButtonWidth = 24
@ -400,7 +400,7 @@ object MainForm: TMainForm
Left = 0
Height = 24
Top = 0
Width = 940
Width = 997
AutoSize = True
ButtonHeight = 24
ButtonWidth = 24
@ -432,7 +432,7 @@ object MainForm: TMainForm
Style = tbsDivider
end
object ToolButton2: TToolButton
Left = 480
Left = 509
Top = 0
Action = AcFileExit
end
@ -561,12 +561,25 @@ object MainForm: TMainForm
Caption = 'ToolButton54'
Style = tbsDivider
end
object ToolButton4: TToolButton
Left = 480
Top = 0
Action = AcSearch
end
object ToolButton55: TToolButton
Left = 504
Height = 24
Top = 0
Width = 5
Caption = 'ToolButton55'
Style = tbsDivider
end
end
object ToolBar3: TToolBar
Left = 0
Height = 28
Top = 50
Width = 940
Width = 997
AutoSize = True
Caption = 'ToolBar3'
Constraints.MinHeight = 28
@ -597,7 +610,7 @@ object MainForm: TMainForm
Left = 144
Height = 24
Top = 0
Width = 796
Width = 853
Align = alClient
BorderSpacing.Bottom = 2
TabOrder = 1
@ -616,7 +629,7 @@ object MainForm: TMainForm
Left = 0
Height = 5
Top = 78
Width = 940
Width = 997
Align = alTop
ResizeAnchor = akTop
end
@ -664,6 +677,7 @@ object MainForm: TMainForm
Caption = 'E&xit'
Hint = 'Exit'
ImageIndex = 0
ShortCut = 32856
end
object AcFontBold: TsFontStyleAction
Category = 'FPSpreadsheet'
@ -1540,12 +1554,20 @@ object MainForm: TMainForm
Hint = 'Delete hyperlink from selected cell'
ImageIndex = 58
end
object AcSearch: TAction
Category = 'Edit'
Caption = 'Search...'
Hint = 'Search for cells'
ImageIndex = 70
OnExecute = AcSearchExecute
ShortCut = 16454
end
end
object ImageList: TImageList
left = 176
top = 312
Bitmap = {
4C69460000001000000010000000003F9300003F9300003F9300003F9424003F
4C69470000001000000010000000003F9300003F9300003F9300003F9424003F
948A003E93CC004095CC004095CC004095CC004095CC004095CC004095CC0040
95CC004095CC00409599003F9400003F9300003F9324003F938A0E4B9CD33F76
C0EC5D90D4FF3365A9FFA0A0A0FFA9A9A9FFA9A9A9FFAAAAAAFFACACACFFAEAE
@ -3785,6 +3807,38 @@ object MainForm: TMainForm
6FFF4C9750FB529C56344E995222FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0060A664315BA25FCC569F
5A4BFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FCDEC102FADCBF97F9D9
BBE3F6D6B8FDF4D3B4FDF1CFAFE3EECBAB97EBC6A602FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FCDEC002FADBBEC0F9E2CDFFFAEC
DEFFF9EEE2FFF9EDE2FFF8E9DAFFF0D5BDFFE7C09FC0E3BC9A02FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FADBBD97F8E2CCFFFAEEE3FFF7E7
D6FFF6E2CEFFF6E1CBFFF6E3D0FFF9EADDFFECCFB5FFDFB69397FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F7D7B9E3F9EBDEFFF7E7D6FFF6E1
CCFFF5E0CAFFF5DEC8FFF5DDC5FFF6E1CBFFF5E2D0FFDBB08CE3FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F4D3B4FDF9EDE1FFF6E1CCFFF5DF
C9FFF5DEC7FFF4DCC4FFF4DBC2FFF4DAC0FFF8E7D6FFD7AA86FDFFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F0CEAEFDF9ECDFFFF5DFC8FFF5DD
C6FFF4DCC3FFF4DAC1FFF3D9BEFFF3D7BDFFF8E6D3FFD3A57FFDFFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00ECC8A8E3F7E7D7FFF6E1CCFFF4DB
C2FFF4DAC0FFF3D8BDFFF3D7BBFFF4DBC2FFF3DEC9FFCD9F7BE7FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E8C3A297EDD0B7FFF8E8D9FFF5DE
C8FFF3D8BDFFF3D6BBFFF4DBC2FFF7E4D2FFDFBB9DFF9D9492F74B84BC27FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E4BD9B02E1B896C0E8C9AEFFF5E1
CDFFF7E5D3FFF7E5D1FFF3DDC8FFDFBA9CFFC7A891FF86AED5FF417DB5EB3977
AF27FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00DDB28F02D9AE8A97D6A9
85E3D3A57FFDD0A07BFDCD9C76E4A2938ADE75A2CCFFABCBE8FF76A4CEFF3070
A8EB286BA327FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00447FB7063C79B1AD6497C5FF9DC1E4FF6699
C7FF1F659DEBFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003272AA062B6DA5AD558DBCFF89B5
DDFF185F97FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF0022669E061B629AAD2267
9DFF115B9387FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00145D9503105A
921AFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00
}
end
@ -4337,6 +4391,48 @@ object MainForm: TMainForm
}
end
end
object MenuItem131: TMenuItem
Caption = '-'
end
object MenuItem132: TMenuItem
Action = AcSearch
Bitmap.Data = {
36040000424D3604000000000000360000002800000010000000100000000100
2000000000000004000064000000640000000000000000000000FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00145D9503105A921AFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF0022669E061B629AAD22679DFF115B9387FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF003272
AA062B6DA5AD558DBCFF89B5DDFF185F97FFFFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00447FB7063C79
B1AD6497C5FF9DC1E4FF6699C7FF1F659DEBFFFFFF00FFFFFF00FFFFFF00FFFF
FF00DDB28F02D9AE8A97D6A985E3D3A57FFDD0A07BFDCD9C76E4A2938ADE75A2
CCFFABCBE8FF76A4CEFF3070A8EB286BA327FFFFFF00FFFFFF00FFFFFF00E4BD
9B02E1B896C0E8C9AEFFF5E1CDFFF7E5D3FFF7E5D1FFF3DDC8FFDFBA9CFFC7A8
91FF86AED5FF417DB5EB3977AF27FFFFFF00FFFFFF00FFFFFF00FFFFFF00E8C3
A297EDD0B7FFF8E8D9FFF5DEC8FFF3D8BDFFF3D6BBFFF4DBC2FFF7E4D2FFDFBB
9DFF9D9492F74B84BC27FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00ECC8
A8E3F7E7D7FFF6E1CCFFF4DBC2FFF4DAC0FFF3D8BDFFF3D7BBFFF4DBC2FFF3DE
C9FFCD9F7BE7FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F0CE
AEFDF9ECDFFFF5DFC8FFF5DDC6FFF4DCC3FFF4DAC1FFF3D9BEFFF3D7BDFFF8E6
D3FFD3A57FFDFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F4D3
B4FDF9EDE1FFF6E1CCFFF5DFC9FFF5DEC7FFF4DCC4FFF4DBC2FFF4DAC0FFF8E7
D6FFD7AA86FDFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F7D7
B9E3F9EBDEFFF7E7D6FFF6E1CCFFF5E0CAFFF5DEC8FFF5DDC5FFF6E1CBFFF5E2
D0FFDBB08CE3FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FADB
BD97F8E2CCFFFAEEE3FFF7E7D6FFF6E2CEFFF6E1CBFFF6E3D0FFF9EADDFFECCF
B5FFDFB69397FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FCDE
C002FADBBEC0F9E2CDFFFAECDEFFF9EEE2FFF9EDE2FFF8E9DAFFF0D5BDFFE7C0
9FC0E3BC9A02FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FCDEC102FADCBF97F9D9BBE3F6D6B8FDF4D3B4FDF1CFAFE3EECBAB97EBC6
A602FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00
}
end
end
object MnuFormat: TMenuItem
Caption = 'Format'

View File

@ -21,6 +21,7 @@ type
AcSettingsCSVParams: TAction;
AcSettingsCurrency: TAction;
AcSettingsFormatSettings: TAction;
AcSearch: TAction;
AcViewInspector: TAction;
ActionList: TActionList;
AcFileExit: TFileExit;
@ -61,6 +62,8 @@ type
MenuItem128: TMenuItem;
MenuItem129: TMenuItem;
MenuItem130: TMenuItem;
MenuItem131: TMenuItem;
MenuItem132: TMenuItem;
MnuSettings: TMenuItem;
MenuItem11: TMenuItem;
MenuItem12: TMenuItem;
@ -299,6 +302,7 @@ type
ToolButton38: TToolButton;
ToolButton39: TToolButton;
TbCommentAdd: TToolButton;
ToolButton4: TToolButton;
ToolButton40: TToolButton;
ToolButton41: TToolButton;
ToolButton42: TToolButton;
@ -317,6 +321,7 @@ type
ToolButton52: TToolButton;
ToolButton53: TToolButton;
ToolButton54: TToolButton;
ToolButton55: TToolButton;
ToolButton6: TToolButton;
ToolButton7: TToolButton;
ToolButton8: TToolButton;
@ -333,6 +338,7 @@ type
AWorkbook: TsWorkbook; var ANumFormatStr: String);
procedure AcRowAddExecute(Sender: TObject);
procedure AcRowDeleteExecute(Sender: TObject);
procedure AcSearchExecute(Sender: TObject);
procedure AcSettingsCSVParamsExecute(Sender: TObject);
procedure AcSettingsCurrencyExecute(Sender: TObject);
procedure AcSettingsFormatSettingsExecute(Sender: TObject);
@ -344,6 +350,7 @@ type
const AHyperlink: TsHyperlink);
private
{ private declarations }
procedure SearchFound(Sender: TObject; ACell: PCell);
procedure UpdateCaption;
protected
procedure ReadFromIni;
@ -364,7 +371,7 @@ uses
LCLIntf, inifiles, uriparser,
fpsUtils, fpsCSV,
sCSVParamsForm, sCurrencyForm, sFormatSettingsForm, sSortParamsForm,
sHyperlinkForm, sNumFormatForm;
sHyperlinkForm, sNumFormatForm, sSearchForm;
function CreateIni: TCustomIniFile;
begin
@ -476,6 +483,14 @@ begin
WorksheetGrid.Row := r;
end;
procedure TMainForm.AcSearchExecute(Sender: TObject);
begin
if SearchForm = nil then
SearchForm := TSearchForm.Create(self);
SearchForm.OnFound := @SearchFound;
SearchForm.Execute(WorkbookSource.Workbook, DefaultSearchParams);
end;
procedure TMainForm.AcSettingsCSVParamsExecute(Sender: TObject);
var
F: TCSVParamsForm;
@ -568,6 +583,22 @@ begin
end;
end;
procedure TMainForm.SearchFound(Sender: TObject; ACell: PCell);
begin
// There could be status message "search string found", here
end;
procedure TMainForm.UpdateCaption;
begin
if WorkbookSource = nil then
Caption := 'demo_ctrls'
else
Caption := Format('demo_ctrls - "%s" [%s]', [
AnsiToUTF8(WorkbookSource.Filename),
GetFileFormatName(WorkbookSource.Workbook.FileFormat)
]);
end;
procedure TMainForm.WriteToIni;
var
ini: TCustomIniFile;
@ -596,16 +627,5 @@ begin
end;
end;
procedure TMainForm.UpdateCaption;
begin
if WorkbookSource = nil then
Caption := 'demo_ctrls'
else
Caption := Format('demo_ctrls - "%s" [%s]', [
AnsiToUTF8(WorkbookSource.Filename),
GetFileFormatName(WorkbookSource.Workbook.FileFormat)
]);
end;
end.

View File

@ -22,6 +22,7 @@ unit fpspreadsheet;
{$ifdef fpc}
{$mode delphi}{$H+}
// {$mode objpas}{$H+}
{$endif}
{$include fps.inc}
@ -389,6 +390,11 @@ type
function GetCell(AddressStr: String): PCell; overload;
function GetCellCount: Cardinal;
function FindNextCellInCol(ARow, ACol: Cardinal): PCell;
function FindNextCellInRow(ARow, ACol: Cardinal): PCell;
function FindPrevCellInCol(ARow, ACol: Cardinal): PCell;
function FindPrevCellInRow(ARow, ACol: Cardinal): PCell;
function GetFirstColIndex(AForceCalculation: Boolean = false): Cardinal;
function GetLastColIndex(AForceCalculation: Boolean = false): Cardinal;
function GetLastColNumber: Cardinal; deprecated 'Use GetLastColIndex';
@ -434,6 +440,10 @@ type
function GetSelectionCount: Integer;
procedure SetSelection(const ASelection: TsCellRangeArray);
// Searching
function Search(ASearchText: String; AOptions: TsSearchOptions;
AStartRow: Cardinal = $FFFFFFFF; AStartCol: Cardinal = $FFFFFFFF): PCell;
// Comments
function FindComment(ACell: PCell): PsComment;
function HasComment(ACell: PCell): Boolean;
@ -711,6 +721,11 @@ type
function UsesColor(AColorIndex: TsColor): Boolean;
*)
{ Searching }
function Search(ASearchText: String; AOptions: TsSearchOptions;
AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = $FFFFFFFF;
AStartCol: Cardinal = $FFFFFFFF): PCell;
{ Utilities }
procedure UpdateCaches;
@ -806,7 +821,7 @@ procedure CopyCellFormat(AFromCell, AToCell: PCell);
implementation
uses
Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser,
Math, StrUtils, DateUtils, TypInfo, lazutf8, lazFileUtils, URIParser, RegExpr,
fpsStrings, uvirtuallayer_ole,
fpsUtils, fpsreaderwriter, fpsCurrency, fpsExprParser,
fpsNumFormatParser;
@ -860,8 +875,8 @@ var
begin
Assert(AFromCell <> nil);
Assert(AToCell <> nil);
sourceSheet := TsWorksheet(AFromCell.Worksheet);
destSheet := TsWorksheet(AToCell.Worksheet);
sourceSheet := TsWorksheet(AFromCell^.Worksheet);
destSheet := TsWorksheet(AToCell^.Worksheet);
if (sourceSheet=nil) or (destSheet=nil) or (sourceSheet.Workbook = destSheet.Workbook) then
AToCell^.FormatIndex := AFromCell^.FormatIndex
else
@ -891,19 +906,19 @@ end;
function CompareCells(Item1, Item2: Pointer): Integer;
begin
result := LongInt(PCell(Item1).Row) - PCell(Item2).Row;
result := LongInt(PCell(Item1)^.Row) - PCell(Item2)^.Row;
if Result = 0 then
Result := LongInt(PCell(Item1).Col) - PCell(Item2).Col;
Result := LongInt(PCell(Item1)^.Col) - PCell(Item2)^.Col;
end;
function CompareRows(Item1, Item2: Pointer): Integer;
begin
Result := LongInt(PRow(Item1).Row) - PRow(Item2).Row;
Result := LongInt(PRow(Item1)^.Row) - PRow(Item2)^.Row;
end;
function CompareCols(Item1, Item2: Pointer): Integer;
begin
Result := LongInt(PCol(Item1).Col) - PCol(Item2).Col;
Result := LongInt(PCol(Item1)^.Col) - PCol(Item2)^.Col;
end;
function CompareMergedCells(Item1, Item2: Pointer): Integer;
@ -1938,6 +1953,56 @@ begin
Result := nil;
end;
function TsWorksheet.FindNextCellInCol(ARow, ACol: Cardinal): PCell;
var
last: Cardinal;
begin
last := GetLastRowIndex;
if ARow = last then
Result := nil
else
repeat
inc(ARow);
Result := FindCell(ARow, ACol);
until (Result <> nil) or (ARow = last);
end;
function TsWorksheet.FindNextCellInRow(ARow, ACol: Cardinal): PCell;
var
last: Cardinal;
begin
last := GetLastColIndex;
if ACol = last then
Result := nil
else
Repeat
inc(ACol);
Result := Findcell(ARow, ACol);
until (Result <> nil) or (ACol = last);
end;
function TsWorksheet.FindPrevCellInCol(ARow, ACol: Cardinal): PCell;
begin
if ARow = 0 then
Result := nil
else
repeat
dec(ARow);
Result := FindCell(ARow, ACol);
until (Result <> nil) or (ARow = 0);
end;
function TsWorksheet.FindPrevCellInRow(ARow, ACol: Cardinal): PCell;
begin
if ACol = 0 then
Result := nil
else
repeat
dec(ACol);
Result := FindCell(ARow, ACol);
until (Result <> nil) or (ACol = 0);
end;
{@@ ----------------------------------------------------------------------------
Obtains an allocated cell at the desired location.
@ -2732,7 +2797,7 @@ begin
if ACell <> nil then
begin
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
for b in fmt.Border do
for b in fmt^.Border do
Result[b] := fmt^.BorderStyles[b];
end;
end;
@ -2812,7 +2877,7 @@ begin
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
if (uffNumberFormat in fmt^.UsedFormattingFields) then
begin
numFmt := Workbook.GetNumberFormat(fmt.NumberFormatIndex);
numFmt := Workbook.GetNumberFormat(fmt^.NumberFormatIndex);
if numFmt <> nil then
begin
ANumFormat := numFmt.NumFormat;
@ -3468,6 +3533,185 @@ begin
FSelection[i] := ASelection[i];
end;
{@@ ----------------------------------------------------------------------------
Searches the cell containing a specified text. The search begins with the
cell "AStartCell". A set of options is respected. Returns a pointer to the
first cell meeting the criteria.
-------------------------------------------------------------------------------}
function TsWorksheet.Search(ASearchText: String; AOptions: TsSearchOptions;
AStartRow: Cardinal = $FFFFFFFF; AStartCol: Cardinal = $FFFFFFFF): PCell;
var
regex: TRegExpr;
cell, startCell: PCell;
r, c: Cardinal;
firstR, firstC, lastR, lastC: Cardinal;
function CellMatches(ACell: PCell): boolean;
var
txt: String;
begin
txt := ReadAsUTF8Text(ACell);
if (soRegularExpr in AOptions) then
Result := regex.Exec(txt)
else
if (soIgnoreCase in AOptions) then
txt := UTF8Lowercase(txt);
if (soCompareFullCell in AOptions) then
exit(txt = ASearchText);
if UTF8Pos(ASearchText, txt) > 0 then
exit(true);
Result := false;
end;
begin
Result := nil;
regex := nil;
firstR := 0;
firstC := 0;
lastR := GetLastRowIndex;
lastC := GetLastColIndex;
// Find first occupied cell to start with
if (soBackward in AOptions) then
begin
if AStartRow = $FFFFFFFF then AStartRow := lastR;
if AStartCol = $FFFFFFFF then AStartCol := lastC;
end else
begin
if AStartRow = $FFFFFFFF then AStartRow := firstR;
if AStartCol = $FFFFFFFF then AStartCol := firstC;
end;
startcell := FindCell(AStartRow, AStartCol);
if startcell = nil then
// Backward search along rows
if (AOptions * [soBackward, soAlongRows] = [soBackward, soAlongRows]) then
begin
startcell := FindPrevCellInRow(AStartRow, AStartCol);
// Not found in this row? Go to previous row
while (startcell = nil) and (AStartRow > 0) do begin
AStartCol := lastC;
dec(AStartRow);
startcell := FindPrevCellInRow(AStartRow, AStartCol);
end;
end
else
// Backward search along columns
if (AOptions * [soBackward, soAlongRows] = [soBackward]) then
begin
startcell := FindPrevCellInCol(AStartRow, AStartCol);
// not found in this column? Go to previous column.
while (startcell = nil) and (AStartcol > 0) do begin
AStartRow := lastR;
dec(AStartCol);
startcell := FindPrevCellInCol(AStartRow, AStartCol);
end;
end
else
// Forward search along rows
if (AOptions * [soBackward, soAlongRows] = [soAlongRows]) then
begin
startcell := FindNextCellInRow(AStartRow, AStartCol);
// Not found in this row? Proceed to next row
while (startcell = nil) and (AStartRow <= lastR) do begin
AStartCol := firstC;
inc(AStartRow);
startcell := FindNextCellInRow(AStartRow, AStartCol);
end;
end
else
// Forward search along columns
if (AOptions * [soBackward, soAlongRows] = []) then
begin
startCell := FindNextCellInCol(AStartRow, AStartCol);
// Not found in this column? Proceed to next column
while (startcell = nil) and (AStartCol <= lastC) do begin
AStartRow := firstR;
inc(AStartCol);
startcell := FindNextCellinCol(AStartRow, AStartCol);
end;
end;
// Still no occupied cell found for starting? Nothing to do...
if startcell = nil then
exit;
// Iterate through cells in order defined by the search options
try
if soRegularExpr in AOptions then
begin
regex := TRegExpr.Create;
regex.Expression := ASearchText
end else
if soIgnoreCase in AOptions then
ASearchText := UTF8Lowercase(ASearchText);
// Perform backward search along rows
if (AOptions * [soBackward, soAlongRows] = [soBackward, soAlongRows]) then
begin
r := startCell^.Row;
for cell in Cells.GetReverseRowEnumerator(r, startCell^.Col) do
if CellMatches(cell) then exit(cell);
if r = 0 then
exit;
while r > 0 do begin
dec(r);
for cell in Cells.GetReverseRowEnumerator(r) do
if CellMatches(cell) then exit(cell);
end;
end
else
// Perform forward search along rows
if (AOptions * [soBackward, soAlongRows] = [soAlongRows]) then
begin
r := startCell^.Row;
for cell in Cells.GetRowEnumerator(r, startCell^.Col) do
if CellMatches(cell) then exit(cell);
if r = lastR then
exit;
while (r < lastR) do
begin
inc(r);
for cell in Cells.GetRowEnumerator(r) do
if CellMatches(cell) then exit(cell);
end;
end
else
// Perform backward search along columns
if (AOptions * [soBackward, soAlongRows] = [soBackward]) then
begin
c := startCell^.Col;
for cell in Cells.GetReverseColEnumerator(c, 0, startCell^.Row) do
if CellMatches(cell) then exit(cell);
if c = 0 then
exit;
while (c > 0) do
begin
dec(c);
for cell in Cells.GetReverseColEnumerator(c) do
if CellMatches(cell) then exit(cell);
end;
end
else
// Perform forward search along columns
if (AOptions * [soBackward, soAlongRows] = []) then
begin
c := startCell^.Col;
for cell in Cells.GetColEnumerator(c, startCell^.Row) do
if CellMatches(cell) then exit(cell);
if c = lastC then
exit;
while (c < lastC) do
begin
inc(c);
for cell in Cells.GetColEnumerator(c) do
if CellMatches(cell) then exit(cell);
end;
end;
finally
if regex <> nil then regex.Free;
end;
end;
{@@ ----------------------------------------------------------------------------
Helper method to update internal caching variables
-------------------------------------------------------------------------------}
@ -6096,7 +6340,7 @@ end;
-------------------------------------------------------------------------------}
procedure TsWorkbook.PrepareBeforeSaving;
var
sheet: TsWorksheet;
sheet: pointer;
begin
// Clear error log
FLog.Clear;
@ -6107,7 +6351,7 @@ begin
// Calculated formulas (if requested)
if (boCalcBeforeSaving in FOptions) then
for sheet in FWorksheets do
sheet.CalcFormulas;
TsWorksheet(sheet).CalcFormulas;
// Abort if virtual mode is active without an event handler
if (boVirtualMode in FOptions) and not Assigned(FOnWriteCellData) then
@ -6119,10 +6363,10 @@ end;
-------------------------------------------------------------------------------}
procedure TsWorkbook.Recalc;
var
sheet: TsWorksheet;
sheet: pointer;
begin
for sheet in FWorksheets do
sheet.CalcFormulas;
TsWorksheet(sheet).CalcFormulas;
end;
{@@ ----------------------------------------------------------------------------
@ -6134,6 +6378,65 @@ begin
TsWorksheet(data).Free;
end;
{@@ ----------------------------------------------------------------------------
Searches the entire workbook for the first cell (after AStartCell) containing
a specified text.
-------------------------------------------------------------------------------}
function TsWorkbook.Search(ASearchText: String; AOptions: TsSearchOptions;
AStartSheet: TsWorksheet = nil; AStartRow: Cardinal = $FFFFFFFF;
AStartCol: Cardinal = $FFFFFFFF): PCell;
var
i, idxSheet: Integer;
sheet: TsWorksheet;
begin
// Setup missing default parameters
if soBackward in AOptions then
begin
if (AStartRow = $FFFFFFFF) and (AStartCol = $FFFFFFFF) and (AStartSheet = nil)
then AStartsheet := GetWorksheetByIndex(GetWorksheetCount-1);
if AStartRow = $FFFFFFFF then
AStartRow := AStartsheet.GetLastRowIndex;
if AStartCol = $FFFFFFFF then
AStartCol := AStartsheet.GetLastColIndex;
end else
begin
if (AStartRow = $FFFFFFFF) and (AStartCol = $FFFFFFFF) and (AStartSheet = nil)
then AStartsheet := GetWorksheetByIndex(0);
if (AStartRow = $FFFFFFFF) then
AStartRow := AStartsheet.GetFirstRowIndex;
if (AStartCol = $FFFFFFFF) then
AStartCol := AStartsheet.GetFirstColIndex;
end;
if AStartSheet = nil then
AStartSheet := ActiveWorksheet;
// Search this worksheet
Result := AStartSheet.Search(ASearchText, AOptions, AStartRow, AStartCol);
if Result <> nil then
exit;
// If not found continue with other sheets in requested order...
idxSheet := GetWorksheetIndex(AStartSheet);
if (soBackward in AOptions) then
// ... backward
for i := idxSheet - 1 downto 0 do
begin
sheet := GetWorksheetByIndex(i);
Result := sheet.Search(ASearchText, AOptions);
if Result <> nil then
exit;
end
else
// ... forward
for i := idxSheet + 1 to GetWorksheetCount-1 do
begin
sheet := GetWorksheetByIndex(i);
Result := sheet.Search(ASearchText, AOptions);
if Result <> nil then
exit;
end;
end;
{@@ ----------------------------------------------------------------------------
Helper method to update internal caching variables
-------------------------------------------------------------------------------}

View File

@ -676,6 +676,12 @@ type
{@@ Pointer to a page layout record }
PsPageLayout = ^TsPageLayout;
{@@ Search option }
TsSearchOption = (soCompareFullCell, soIgnoreCase, soRegularExpr,
soBackward, soAlongRows);
TsSearchOptions = set of TsSearchOption;
const
{@@ Indexes to be used for the various headers and footers }
HEADER_FOOTER_INDEX_FIRST = 0;