You've already forked lazarus-ccr
fpspreadsheet: Rewrite "ParseCellString" to allow for multiletter column addresses.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3091 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -231,10 +231,11 @@
|
|||||||
<Unit13>
|
<Unit13>
|
||||||
<Filename Value="..\..\fpsutils.pas"/>
|
<Filename Value="..\..\fpsutils.pas"/>
|
||||||
<UnitName Value="fpsutils"/>
|
<UnitName Value="fpsutils"/>
|
||||||
|
<IsVisibleTab Value="True"/>
|
||||||
<EditorIndex Value="2"/>
|
<EditorIndex Value="2"/>
|
||||||
<WindowIndex Value="0"/>
|
<WindowIndex Value="0"/>
|
||||||
<TopLine Value="62"/>
|
<TopLine Value="279"/>
|
||||||
<CursorPos X="1" Y="63"/>
|
<CursorPos X="52" Y="316"/>
|
||||||
<UsageCount Value="69"/>
|
<UsageCount Value="69"/>
|
||||||
<Loaded Value="True"/>
|
<Loaded Value="True"/>
|
||||||
</Unit13>
|
</Unit13>
|
||||||
@ -263,7 +264,6 @@
|
|||||||
<Unit17>
|
<Unit17>
|
||||||
<Filename Value="..\..\xlsbiff8.pas"/>
|
<Filename Value="..\..\xlsbiff8.pas"/>
|
||||||
<UnitName Value="xlsbiff8"/>
|
<UnitName Value="xlsbiff8"/>
|
||||||
<IsVisibleTab Value="True"/>
|
|
||||||
<EditorIndex Value="3"/>
|
<EditorIndex Value="3"/>
|
||||||
<WindowIndex Value="0"/>
|
<WindowIndex Value="0"/>
|
||||||
<TopLine Value="847"/>
|
<TopLine Value="847"/>
|
||||||
|
@ -784,6 +784,8 @@ resourcestring
|
|||||||
lpInvalidNumberFormat = 'Trying to use an incompatible number format.';
|
lpInvalidNumberFormat = 'Trying to use an incompatible number format.';
|
||||||
lpNoValidNumberFormatString = 'No valid number format string.';
|
lpNoValidNumberFormatString = 'No valid number format string.';
|
||||||
lpNoValidDateTimeFormatString = 'No valid date/time format string.';
|
lpNoValidDateTimeFormatString = 'No valid date/time format string.';
|
||||||
|
lpNoValidCellAddress = '"%s" is not a valid cell address.';
|
||||||
|
lpNoValidCellRangeAddress = '"%s" is not a valid cell range address.';
|
||||||
lpIllegalNumberFormat = 'Illegal number format.';
|
lpIllegalNumberFormat = 'Illegal number format.';
|
||||||
lpSpecifyNumberOfParams = 'Specify number of parameters for function %s';
|
lpSpecifyNumberOfParams = 'Specify number of parameters for function %s';
|
||||||
lpIncorrectParamCount = 'Funtion %s requires at least %d and at most %d parameters.';
|
lpIncorrectParamCount = 'Funtion %s requires at least %d and at most %d parameters.';
|
||||||
@ -3793,7 +3795,7 @@ var
|
|||||||
flags: TsRelFlags;
|
flags: TsRelFlags;
|
||||||
begin
|
begin
|
||||||
if not ParseCellString(ACellAddress, r, c, flags) then
|
if not ParseCellString(ACellAddress, r, c, flags) then
|
||||||
raise Exception.CreateFmt('"%s" is not a valid cell address.', [ACellAddress]);
|
raise Exception.CreateFmt(lpNoValidCellAddress, [ACellAddress]);
|
||||||
Result := RPNCellRef(r,c, flags, ANext);
|
Result := RPNCellRef(r,c, flags, ANext);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -3826,7 +3828,7 @@ var
|
|||||||
flags: TsRelFlags;
|
flags: TsRelFlags;
|
||||||
begin
|
begin
|
||||||
if not ParseCellRangeString(ACellRangeAddress, r1,c1, r2,c2, flags) then
|
if not ParseCellRangeString(ACellRangeAddress, r1,c1, r2,c2, flags) then
|
||||||
raise Exception.CreateFmt('"%s" is not a valid cell range address.', [ACellRangeAddress]);
|
raise Exception.CreateFmt(lpNoValidCellRangeAddress, [ACellRangeAddress]);
|
||||||
Result := RPNCellRange(r1,c1, r2,c2, flags, ANext);
|
Result := RPNCellRange(r1,c1, r2,c2, flags, ANext);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ end;
|
|||||||
|
|
||||||
{@@
|
{@@
|
||||||
Parses strings like A5:C10 into a range selection information.
|
Parses strings like A5:C10 into a range selection information.
|
||||||
Return also information on relative/absolute cells.
|
Returns in AFlags also information on relative/absolute cells.
|
||||||
}
|
}
|
||||||
function ParseCellRangeString(const AStr: string;
|
function ParseCellRangeString(const AStr: string;
|
||||||
var AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Integer;
|
var AFirstCellRow, AFirstCellCol, ALastCellRow, ALastCellCol: Integer;
|
||||||
@ -283,6 +283,7 @@ function ParseCellRangeString(const AStr: string;
|
|||||||
var
|
var
|
||||||
p: Integer;
|
p: Integer;
|
||||||
s: String;
|
s: String;
|
||||||
|
f: TsRelFlags;
|
||||||
begin
|
begin
|
||||||
Result := True;
|
Result := True;
|
||||||
|
|
||||||
@ -292,109 +293,93 @@ begin
|
|||||||
|
|
||||||
// Analyze part after the colon
|
// Analyze part after the colon
|
||||||
s := copy(AStr, p+1, Length(AStr));
|
s := copy(AStr, p+1, Length(AStr));
|
||||||
Result := ParseCellString(s, ALastCellRow, ALastCellCol, AFlags);
|
Result := ParseCellString(s, ALastCellRow, ALastCellCol, f);
|
||||||
if not Result then exit;
|
if not Result then exit;
|
||||||
if (rfRelRow in AFlags) then begin
|
|
||||||
Include(AFlags, rfRelRow2);
|
|
||||||
Exclude(AFlags, rfRelRow);
|
|
||||||
end;
|
|
||||||
if (rfRelCol in AFlags) then begin
|
|
||||||
Include(AFlags, rfRelCol2);
|
|
||||||
Exclude(AFlags, rfRelCol);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Analyze part before the colon
|
// Analyze part before the colon
|
||||||
s := copy(AStr, 1, p-1);
|
s := copy(AStr, 1, p-1);
|
||||||
Result := ParseCellString(s, AFirstCellRow, AFirstCellCol, AFlags);
|
Result := ParseCellString(s, AFirstCellRow, AFirstCellCol, AFlags);
|
||||||
|
|
||||||
|
// Add flags of 2nd part
|
||||||
|
if rfRelRow in f then Include(AFlags, rfRelRow2);
|
||||||
|
if rfRelCol in f then Include(AFlags, rfRelCol2);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
{@@
|
{@@
|
||||||
Parses a cell string, like 'A1' into zero-based column and row numbers
|
Parses a cell string, like 'A1' into zero-based column and row numbers
|
||||||
|
Note that there can be several letters to address for more than 26 columns.
|
||||||
The parser is a simple state machine, with the following states:
|
|
||||||
|
|
||||||
0 - Reading Column part 1 (necesserely needs a letter)
|
|
||||||
1 - Reading Column part 2, but could be the first number as well
|
|
||||||
2 - Reading Row
|
|
||||||
|
|
||||||
'AFlags' indicates relative addresses.
|
'AFlags' indicates relative addresses.
|
||||||
|
|
||||||
|
Example "AMP$200" --> (rel) column 1029 (= 26*26*1 + 26*16 + 26 - 1)
|
||||||
|
(abs) row = 199 (abs)
|
||||||
}
|
}
|
||||||
function ParseCellString(const AStr: string; var ACellRow, ACellCol: Integer;
|
function ParseCellString(const AStr: String; var ACellRow, ACellCol: Integer;
|
||||||
var AFlags: TsRelFlags): Boolean;
|
var AFlags: TsRelFlags): Boolean;
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
state: Integer;
|
|
||||||
Col, Row: string;
|
|
||||||
lChar: Char;
|
|
||||||
isAbs: Boolean;
|
|
||||||
const
|
|
||||||
cLetters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
|
||||||
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z'];
|
|
||||||
cDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
|
||||||
begin
|
|
||||||
// Starting state
|
|
||||||
Result := True;
|
|
||||||
state := 0;
|
|
||||||
Col := '';
|
|
||||||
Row := '';
|
|
||||||
AFlags := [rfRelCol, rfRelRow];
|
|
||||||
isAbs := false;
|
|
||||||
|
|
||||||
// Separates the string into a row and a col
|
function Scan(AStartPos: Integer): Boolean;
|
||||||
for i := 1 to Length(AStr) do
|
const
|
||||||
|
LETTERS = ['A'..'Z'];
|
||||||
|
DIGITS = ['0'..'9'];
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
isAbs: Boolean;
|
||||||
begin
|
begin
|
||||||
lChar := AStr[i];
|
Result := false;
|
||||||
|
|
||||||
if lChar = '$' then begin
|
i := AStartPos;
|
||||||
if isAbs then
|
// Scan letters
|
||||||
exit(false);
|
while (i <= Length(AStr)) do begin
|
||||||
isAbs := true;
|
if (UpCase(AStr[i]) in LETTERS) then begin
|
||||||
continue;
|
ACellCol := ord(UpCase(AStr[i])) - ord('A') + 1 + ACellCol * 26;
|
||||||
end;
|
inc(i);
|
||||||
|
|
||||||
case state of
|
|
||||||
|
|
||||||
0:
|
|
||||||
begin
|
|
||||||
if lChar in cLetters then
|
|
||||||
begin
|
|
||||||
Col := lChar;
|
|
||||||
if isAbs then
|
|
||||||
Exclude(AFlags, rfRelCol);
|
|
||||||
isAbs := false;
|
|
||||||
state := 1;
|
|
||||||
end
|
end
|
||||||
else Exit(False);
|
else
|
||||||
|
if (AStr[i] in DIGITS) or (AStr[i] = '$') then
|
||||||
|
break
|
||||||
|
else begin
|
||||||
|
ACellCol := 0;
|
||||||
|
exit; // Only letters or $ allowed
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
if AStartPos = 1 then Include(AFlags, rfRelCol);
|
||||||
|
|
||||||
1:
|
isAbs := (AStr[i] = '$');
|
||||||
begin
|
if isAbs then inc(i);
|
||||||
if lChar in cLetters then
|
|
||||||
Col := Col + lChar
|
// Scan digits
|
||||||
else if lChar in cDigits then
|
while (i <= Length(AStr)) do begin
|
||||||
begin
|
if (AStr[i] in DIGITS) then begin
|
||||||
Row := lChar;
|
ACellRow := (ord(AStr[i]) - ord('0')) + ACellRow * 10;
|
||||||
if isAbs then
|
inc(i);
|
||||||
Exclude(AFlags, rfRelRow);
|
|
||||||
isAbs := false;
|
|
||||||
state := 2;
|
|
||||||
end
|
end
|
||||||
else Exit(False);
|
else begin
|
||||||
|
ACellCol := 0;
|
||||||
|
ACellRow := 0;
|
||||||
|
AFlags := [];
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
2:
|
dec(ACellCol);
|
||||||
begin
|
dec(ACellRow);
|
||||||
if lChar in cDigits then Row := Row + lChar
|
if not isAbs then Include(AFlags, rfRelRow);
|
||||||
else Exit(False);
|
|
||||||
end;
|
|
||||||
|
|
||||||
end;
|
Result := true;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Now parses each separetely
|
begin
|
||||||
ParseCellRowString(Row, ACellRow);
|
ACellCol := 0;
|
||||||
ParseCellColString(Col, ACellCol);
|
ACellRow := 0;
|
||||||
|
AFlags := [];
|
||||||
|
|
||||||
|
if AStr = '' then
|
||||||
|
Exit(false);
|
||||||
|
|
||||||
|
if (AStr[1] = '$') then
|
||||||
|
Result := Scan(2)
|
||||||
|
else
|
||||||
|
Result := Scan(1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ for compatibility with old version which does not return flags for relative
|
{ for compatibility with old version which does not return flags for relative
|
||||||
|
Reference in New Issue
Block a user