fpspreadsheet: Fix cell row and column enumerator sometimes failing

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4036 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-03-16 00:05:56 +00:00
parent 3a9150acf3
commit 95edadbd29
8 changed files with 109 additions and 55 deletions

View File

@ -1,7 +1,7 @@
object MainForm: TMainForm object MainForm: TMainForm
Left = 503 Left = 338
Height = 621 Height = 621
Top = 157 Top = 118
Width = 940 Width = 940
Caption = 'demo_ctrls' Caption = 'demo_ctrls'
ClientHeight = 601 ClientHeight = 601
@ -80,7 +80,7 @@ object MainForm: TMainForm
'FileName=' 'FileName='
'FileFormat=sfExcel8' 'FileFormat=sfExcel8'
'ActiveWorksheet=Sheet1' 'ActiveWorksheet=Sheet1'
'Options=boAutoCalc, boCalcBeforeSaving, boReadFormulas' 'Options=boBufStream, boCalcBeforeSaving, boReadFormulas'
'FormatSettings=' 'FormatSettings='
' ThousandSeparator=.' ' ThousandSeparator=.'
' DecimalSeparator=.' ' DecimalSeparator=.'

View File

@ -320,7 +320,6 @@ implementation
{$R *.lfm} {$R *.lfm}
uses uses
LCLProc, // debugln
fpsUtils, fpsCSV, fpsUtils, fpsCSV,
sCSVParamsForm, sCurrencyForm, sFormatSettingsForm, sSortParamsForm; sCSVParamsForm, sCurrencyForm, sFormatSettingsForm, sSortParamsForm;
@ -329,8 +328,6 @@ uses
{ Loads the spreadsheet file selected by the AcFileOpen action } { Loads the spreadsheet file selected by the AcFileOpen action }
procedure TMainForm.AcFileOpenAccept(Sender: TObject); procedure TMainForm.AcFileOpenAccept(Sender: TObject);
var
t: TTime;
begin begin
WorkbookSource.AutodetectFormat := false; WorkbookSource.AutodetectFormat := false;
case AcFileOpen.Dialog.FilterIndex of case AcFileOpen.Dialog.FilterIndex of
@ -343,10 +340,7 @@ begin
7: WorkbookSource.FileFormat := sfOpenDocument; // Open/LibreOffice 7: WorkbookSource.FileFormat := sfOpenDocument; // Open/LibreOffice
8: WorkbookSource.FileFormat := sfCSV; // Text files 8: WorkbookSource.FileFormat := sfCSV; // Text files
end; end;
t := now;
WorkbookSource.FileName := UTF8ToAnsi(AcFileOpen.Dialog.FileName); // this loads the file WorkbookSource.FileName := UTF8ToAnsi(AcFileOpen.Dialog.FileName); // this loads the file
t := (now - t)*24*3600;
DebugLn(Format('Loading time for %s: %.3f sec', [AcFileOpen.Dialog.FileName, t]));
UpdateCaption; UpdateCaption;
end; end;

View File

@ -365,7 +365,7 @@ implementation
uses uses
TypInfo, LCLIntf, LCLType, LCLVersion, fpcanvas, Buttons, TypInfo, LCLIntf, LCLType, LCLVersion, fpcanvas, Buttons,
fpsutils, fpscsv, fpsNumFormat, fpsutils, fpscsv, fpsNumFormat,
sFormatSettingsForm, sCSVParamsForm, sSortParamsForm, sfCurrencyForm; sFormatSettingsForm, sCSVParamsForm, sSortParamsForm, sCurrencyForm;
const const
DROPDOWN_COUNT = 24; DROPDOWN_COUNT = 24;

View File

@ -24,6 +24,7 @@ type
FCurrentNode: TAVLTreeNode; FCurrentNode: TAVLTreeNode;
FTree: TsRowColAVLTree; FTree: TsRowColAVLTree;
FStartRow, FEndRow, FStartCol, FEndCol: Cardinal; FStartRow, FEndRow, FStartCol, FEndCol: Cardinal;
FDone: Boolean;
FReverse: Boolean; FReverse: Boolean;
function GetCurrent: PsRowCol; function GetCurrent: PsRowCol;
public public
@ -175,7 +176,7 @@ type
implementation implementation
uses uses
{%H-}Math, Math,
fpsUtils; fpsUtils;
@ -199,24 +200,23 @@ constructor TsRowColEnumerator.Create(ATree: TsRowColAVLTree;
begin begin
FTree := ATree; FTree := ATree;
FReverse := AReverse; FReverse := AReverse;
// Rearrange col/row indexes such that iteration always begins with "StartXXX"
if AStartRow <= AEndRow then if AStartRow <= AEndRow then
begin begin
FStartRow := IfThen(AReverse, AEndRow, AStartRow); FStartRow := AStartRow;
FEndRow := IfThen(AReverse, AStartRow, AEndRow); FEndRow := AEndRow;
end else end else
begin begin
FStartRow := IfThen(AReverse, AStartRow, AEndRow); FStartRow := AEndRow;
FEndRow := IfThen(AReverse, AEndRow, AStartRow); FEndRow := AStartRow;
end; end;
if AStartCol <= AEndCol then if AStartCol <= AEndCol then
begin begin
FStartCol := IfThen(AReverse, AEndCol, AStartCol); FStartCol := AStartCol;
FEndCol := IfThen(AReverse, AStartCol, AEndCol); FEndCol := AEndCol;
end else end else
begin begin
FStartCol := IfThen(AReverse, AStartCol, AEndCol); FStartCol := AEndCol;
FEndCol := IfThen(AReverse, AEndCol, AStartCol); FEndCol := AStartCol;
end; end;
end; end;
@ -234,46 +234,68 @@ begin
end; end;
function TsRowColEnumerator.MoveNext: Boolean; function TsRowColEnumerator.MoveNext: Boolean;
var
curr: PsRowCol;
begin begin
Result := false;
if FCurrentNode <> nil then begin if FCurrentNode <> nil then begin
if FReverse then if FReverse then
begin begin
FCurrentNode := FTree.FindPrecessor(FCurrentNode); FCurrentNode := FTree.FindPrecessor(FCurrentNode);
if FCurrentNode <> nil then
begin
curr := PsRowCol(FCurrentNode.Data);
if not InRange(curr^.Col, FStartCol, FEndCol) then
while (FCurrentNode <> nil) and while (FCurrentNode <> nil) and
( (Current^.Col < FEndCol) or (Current^.Col > FStartCol) or not InRange(curr^.Col, FStartCol, FEndCol) and (curr^.Row >= FStartRow)
(Current^.Row < FEndRow) or (Current^.Row > FStartRow) ) do begin
do
FCurrentNode := FTree.FindPrecessor(FCurrentNode); FCurrentNode := FTree.FindPrecessor(FCurrentNode);
if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data);
end;
end;
end else end else
begin begin
FCurrentNode := FTree.FindSuccessor(FCurrentNode); FCurrentNode := FTree.FindSuccessor(FCurrentNode);
if FCurrentNode <> nil then
begin
curr := PsRowCol(FCurrentNode.Data);
if not InRange(curr^.Col, FStartCol, FEndCol) then
while (FCurrentNode <> nil) and while (FCurrentNode <> nil) and
( (Current^.Col < FStartCol) or (Current^.Col > FEndCol) or not InRange(curr^.Col, FStartCol, FEndCol) and (curr^.Row <= FEndRow)
(Current^.Row < FStartRow) or (Current^.Row > FEndRow) ) do begin
do
FCurrentNode := FTree.FindSuccessor(FCurrentNode); FCurrentNode := FTree.FindSuccessor(FCurrentNode);
if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data);
end; end;
end;
end;
Result := (FCurrentNode <> nil) and
InRange(curr^.Col, FStartCol, FEndCol) and
InRange(curr^.Row, FStartRow, FEndRow);
end else end else
begin begin
if FReverse then if FReverse then
begin begin
FCurrentNode := FTree.FindHighest; FCurrentNode := FTree.FindHighest;
curr := PsRowCol(FCurrentNode.Data);
while (FCurrentNode <> nil) and while (FCurrentNode <> nil) and
( (Current^.Row < FEndRow) or (Current^.Row > FStartRow) or not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol))
(Current^.Col < FEndCol) or (Current^.Col > FStartCol) ) do begin
do
FCurrentNode := FTree.FindPrecessor(FCurrentNode); FCurrentNode := FTree.FindPrecessor(FCurrentNode);
if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data);
end;
end else end else
begin begin
FCurrentNode := FTree.FindLowest; FCurrentNode := FTree.FindLowest;
curr := Current;
while (FCurrentNode <> nil) and while (FCurrentNode <> nil) and
( (Current^.Row < FStartRow) or (Current^.Row > FEndRow) or not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol))
(Current^.Col < FStartCol) or (Current^.Col > FEndCol) ) do begin
do
FCurrentNode := FTree.FindSuccessor(FCurrentNode); FCurrentNode := FTree.FindSuccessor(FCurrentNode);
if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data);
end; end;
end; end;
Result := FCurrentNode <> nil; Result := (FCurrentNode <> nil);
end;
end; end;

View File

@ -442,8 +442,8 @@ procedure Register;
implementation implementation
uses uses
Types, Math, TypInfo, LCLType, Dialogs, Forms, Types, Math, TypInfo, LCLType, LCLProc, Dialogs, Forms,
fpsStrings, fpsUtils; //, fpSpreadsheetGrid, fpSpreadsheetChart; fpsStrings, fpsUtils;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -453,7 +453,7 @@ uses
procedure Register; procedure Register;
begin begin
RegisterComponents('FPSpreadsheet', [ RegisterComponents('FPSpreadsheet', [
TsWorkbookSource, TsWorkbookTabControl, //TsWorksheetGrid, TsWorkbookSource, TsWorkbookTabControl,
TsCellEdit, TsCellIndicator, TsCellCombobox, TsCellEdit, TsCellIndicator, TsCellCombobox,
TsSpreadsheetInspector TsSpreadsheetInspector
]); ]);
@ -816,22 +816,30 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorkbookSource.InternalLoadFromFile(AFileName: string; procedure TsWorkbookSource.InternalLoadFromFile(AFileName: string;
AAutoDetect: Boolean; AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer = 0); AAutoDetect: Boolean; AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer = 0);
var
t: TTime;
begin begin
// Create a new empty workbook // Create a new empty workbook
t := now;
InternalCreateNewWorkbook; InternalCreateNewWorkbook;
DebugLn(Format('[Timer] Create workbook: %.3f sec', [(now-t)*24*3600]));
DisableControls; DisableControls;
try try
t := Now;
// Read workbook from file and get worksheet // Read workbook from file and get worksheet
if AAutoDetect then if AAutoDetect then
FWorkbook.ReadFromFile(AFileName) FWorkbook.ReadFromFile(AFileName)
else else
FWorkbook.ReadFromFile(AFileName, AFormat); FWorkbook.ReadFromFile(AFileName, AFormat);
DebugLn(Format('[Timer] Read file: %.3f sec', [(now-t)*24*3600]))
finally finally
EnableControls; EnableControls;
end; end;
t := now;
SelectWorksheet(FWorkbook.GetWorkSheetByIndex(AWorksheetIndex)); SelectWorksheet(FWorkbook.GetWorkSheetByIndex(AWorksheetIndex));
DebugLn(Format('[Timer] Select worksheet: %.3f sec', [(now-t)*24*3600]));
// If required, display loading error message // If required, display loading error message
if FWorkbook.ErrorMsg <> '' then if FWorkbook.ErrorMsg <> '' then

View File

@ -26,7 +26,6 @@ interface
uses uses
Classes, SysUtils, LResources, Classes, SysUtils, LResources,
Forms, Controls, Graphics, Dialogs, Grids, ExtCtrls, Forms, Controls, Graphics, Dialogs, Grids, ExtCtrls,
LCLVersion,
fpstypes, fpspreadsheet, fpspreadsheetctrls; fpstypes, fpspreadsheet, fpspreadsheetctrls;
type type
@ -53,6 +52,7 @@ type
FEditText: String; FEditText: String;
FOldEditText: String; FOldEditText: String;
FLockCount: Integer; FLockCount: Integer;
FLockSetup: Integer;
FEditing: Boolean; FEditing: Boolean;
FCellFont: TFont; FCellFont: TFont;
FAutoCalc: Boolean; FAutoCalc: Boolean;
@ -584,7 +584,7 @@ procedure Register;
implementation implementation
uses uses
Types, LCLType, LCLIntf, Math, Types, LCLType, LCLIntf, LCLProc, Math,
fpCanvas, fpsStrings, fpsUtils, fpsVisualUtils; fpCanvas, fpsStrings, fpsUtils, fpsVisualUtils;
const const
@ -3289,6 +3289,7 @@ begin
FOwnedWorksheet := AWorksheet; FOwnedWorksheet := AWorksheet;
if FOwnedWorksheet <> nil then begin if FOwnedWorksheet <> nil then begin
inc(FLockSetup);
FOwnedWorksheet.OnChangeCell := @ChangedCellHandler; FOwnedWorksheet.OnChangeCell := @ChangedCellHandler;
FOwnedWorksheet.OnChangeFont := @ChangedFontHandler; FOwnedWorksheet.OnChangeFont := @ChangedFontHandler;
ShowHeaders := (soShowHeaders in Worksheet.Options); ShowHeaders := (soShowHeaders in Worksheet.Options);
@ -3302,6 +3303,7 @@ begin
end; end;
Row := FrozenRows; Row := FrozenRows;
Col := FrozenCols; Col := FrozenCols;
dec(FLockSetup);
end; end;
Setup; Setup;
end; end;
@ -3316,6 +3318,8 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string; procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string;
AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer); AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer);
var
t: TTime;
begin begin
if FOwnsWorkbook then if FOwnsWorkbook then
FreeAndNil(FOwnedWorkbook); FreeAndNil(FOwnedWorkbook);
@ -3326,9 +3330,17 @@ begin
begin begin
BeginUpdate; BeginUpdate;
try try
t := now;
CreateNewWorkbook; CreateNewWorkbook;
DebugLn(Format('[Timer] Create workbook: %.3f sec', [(now - t)*24*3600]));
t := now;
Workbook.ReadFromFile(AFileName, AFormat); Workbook.ReadFromFile(AFileName, AFormat);
DebugLn(Format('[Timer] Read file: %.3f sec', [(now - t)*24*3600]));
t := now;
LoadFromWorksheet(Workbook.GetWorksheetByIndex(AWorksheetIndex)); LoadFromWorksheet(Workbook.GetWorksheetByIndex(AWorksheetIndex));
DebugLn(Format('[Timer] Load into grid: %.3f sec', [(now - t)*24*3600]));
finally finally
EndUpdate; EndUpdate;
end; end;
@ -3344,6 +3356,8 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string; procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string;
AWorksheetIndex: Integer); AWorksheetIndex: Integer);
var
t: TTime;
begin begin
if FOwnsWorkbook then if FOwnsWorkbook then
FreeAndNil(FOwnedWorkbook); FreeAndNil(FOwnedWorkbook);
@ -3354,9 +3368,17 @@ begin
begin begin
BeginUpdate; BeginUpdate;
try try
t := now;
CreateNewWorkbook; CreateNewWorkbook;
DebugLn(Format('[Timer] Create workbook: %.3f sec', [(now - t)*24*3600]));
t := now;
Workbook.ReadFromFile(AFilename); Workbook.ReadFromFile(AFilename);
DebugLn(Format('[Timer] Read file: %.3f sec', [(now - t)*24*3600]));
t := now;
LoadFromWorksheet(Workbook.GetWorksheetByIndex(AWorksheetIndex)); LoadFromWorksheet(Workbook.GetWorksheetByIndex(AWorksheetIndex));
DebugLn(Format('[Timer] Load into grid: %.3f sec', [(now - t)*24*3600]));
finally finally
EndUpdate; EndUpdate;
end; end;
@ -3382,6 +3404,7 @@ begin
begin begin
if (Worksheet <> nil) then if (Worksheet <> nil) then
begin begin
inc(FLockSetup);
ShowHeaders := (soShowHeaders in Worksheet.Options); ShowHeaders := (soShowHeaders in Worksheet.Options);
ShowGridLines := (soShowGridLines in Worksheet.Options); ShowGridLines := (soShowGridLines in Worksheet.Options);
if (soHasFrozenPanes in Worksheet.Options) then begin if (soHasFrozenPanes in Worksheet.Options) then begin
@ -3391,6 +3414,7 @@ begin
FrozenCols := 0; FrozenCols := 0;
FrozenRows := 0; FrozenRows := 0;
end; end;
dec(FLockSetup);
end; end;
Setup; Setup;
end; end;
@ -3696,6 +3720,9 @@ begin
if csLoading in ComponentState then if csLoading in ComponentState then
exit; exit;
if FLockSetup > 0 then
exit;
if (Worksheet = nil) or (Worksheet.GetCellCount = 0) then begin if (Worksheet = nil) or (Worksheet.GetCellCount = 0) then begin
if ShowHeaders then begin if ShowHeaders then begin
ColCount := FInitColCount + 1; //2; ColCount := FInitColCount + 1; //2;
@ -3928,6 +3955,7 @@ var
lRow: PRow; lRow: PRow;
h: Integer; h: Integer;
begin begin
BeginUpdate;
if AStartIndex <= 0 then AStartIndex := FHeaderCount; if AStartIndex <= 0 then AStartIndex := FHeaderCount;
for i := AStartIndex to RowCount-1 do begin for i := AStartIndex to RowCount-1 do begin
h := CalcAutoRowHeight(i); h := CalcAutoRowHeight(i);
@ -3939,6 +3967,7 @@ begin
end; end;
RowHeights[i] := h; RowHeights[i] := h;
end; end;
EndUpdate;
end; end;

View File

@ -212,15 +212,15 @@ begin
actual := MyWorksheet.ReadAsUTF8Text(cell); actual := MyWorksheet.ReadAsUTF8Text(cell);
if row = 0 then begin if row = 0 then begin
// an originally blank cell shows the hyperlink.Target. But Worksheet.WriteHyperlink // An originally blank cell shows the hyperlink.Target.
// removes the "file:///" protocol // But Worksheet.WriteHyperlink removes the "file:///" protocol
expected := hyperlink.Target; expected := hyperlink.Target;
if pos('file:', SollLinks[ATestMode])=1 then begin if pos('file:', SollLinks[ATestMode])=1 then
Delete(expected, 1, Length('file:///')); Delete(expected, 1, Length('file:///'))
ForcePathDelims(expected);
end;
end else end else
expected := SollCellContent[row]; expected := SollCellContent[row];
FixHyperlinkPathDelims(expected);
FixHyperlinkPathDelims(actual);
CheckEquals(expected, actual, CheckEquals(expected, actual,
'Test saved hyperlink cell text, cell '+ CellNotation(MyWorksheet, row, col)); 'Test saved hyperlink cell text, cell '+ CellNotation(MyWorksheet, row, col));

View File

@ -2417,7 +2417,6 @@ begin
lCell.BoolValue := value <> 0; lCell.BoolValue := value <> 0;
end; end;
WriteCellToStream(AStream, @lCell); WriteCellToStream(AStream, @lCell);
// WriteCellCallback(@lCell, AStream);
varClear(value); varClear(value);
end; end;
AppendToStream(AStream, AppendToStream(AStream,
@ -2437,16 +2436,18 @@ begin
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
'<row r="%d" spans="%d:%d"%s>', [r+1, c1+1, c2+1, rh])); '<row r="%d" spans="%d:%d"%s>', [r+1, c1+1, c2+1, rh]));
// Write cells belonging to this row. // Write cells belonging to this row.
{ // Strange: the RowEnumerator is very slow here... ?!
for cell in AWorksheet.Cells.GetRowEnumerator(r) do for cell in AWorksheet.Cells.GetRowEnumerator(r) do
WriteCellToStream(AStream, cell); WriteCellToStream(AStream, cell);
{ }
for c := c1 to c2 do begin for c := c1 to c2 do begin
cell := AWorksheet.FindCell(r, c); cell := AWorksheet.FindCell(r, c);
if Assigned(cell) then begin if Assigned(cell) then
WriteCellCallback(cell, AStream); WriteCellToStream(AStream, cell);
end; end;
end;
}
AppendToStream(AStream, AppendToStream(AStream,
'</row>'); '</row>');
end; end;