You've already forked lazarus-ccr
fpspreadsheet: Avoid duplicate code in TsWorkbookChartSource. Fix crashes of the chartsource when used worksheets are renamed or deleted. More refined notification of visual controls.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3867 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -28,10 +28,13 @@
|
||||
<FormatVersion Value="1"/>
|
||||
</local>
|
||||
</RunParams>
|
||||
<RequiredPackages Count="1">
|
||||
<RequiredPackages Count="2">
|
||||
<Item1>
|
||||
<PackageName Value="LCL"/>
|
||||
<PackageName Value="TAChartLazarusPkg"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<PackageName Value="LCL"/>
|
||||
</Item2>
|
||||
</RequiredPackages>
|
||||
<Units Count="2">
|
||||
<Unit0>
|
||||
|
@ -7,7 +7,7 @@ uses
|
||||
cthreads,
|
||||
{$ENDIF}{$ENDIF}
|
||||
Interfaces, // this includes the LCL widgetset
|
||||
Forms, main;
|
||||
Forms, tachartlazaruspkg, main;
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
@ -51,10 +51,13 @@
|
||||
<FormatVersion Value="1"/>
|
||||
</local>
|
||||
</RunParams>
|
||||
<RequiredPackages Count="1">
|
||||
<RequiredPackages Count="2">
|
||||
<Item1>
|
||||
<PackageName Value="LCL"/>
|
||||
<PackageName Value="TAChartLazarusPkg"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<PackageName Value="LCL"/>
|
||||
</Item2>
|
||||
</RequiredPackages>
|
||||
<Units Count="2">
|
||||
<Unit0>
|
||||
|
@ -7,7 +7,7 @@ uses
|
||||
cthreads,
|
||||
{$ENDIF}{$ENDIF}
|
||||
Interfaces, // this includes the LCL widgetset
|
||||
Forms, mainfrm
|
||||
Forms, tachartlazaruspkg, mainfrm
|
||||
{ you can add units after this };
|
||||
|
||||
{$R *.res}
|
||||
|
@ -969,8 +969,10 @@ type
|
||||
FOnWriteCellData: TsWorkbookWriteCellDataEvent;
|
||||
FOnReadCellData: TsWorkbookReadCellDataEvent;
|
||||
FOnChangeWorksheet: TsWorksheetEvent;
|
||||
FOnRenameWorksheet: TsWorksheetEvent;
|
||||
FOnAddWorksheet: TsWorksheetEvent;
|
||||
FOnRemoveWorksheet: TsRemoveWorksheetEvent;
|
||||
FOnRemovingWorksheet: TsWorksheetEvent;
|
||||
FOnSelectWorksheet: TsWorksheetEvent;
|
||||
FOnChangePalette: TNotifyEvent;
|
||||
FFileName: String;
|
||||
@ -1033,6 +1035,14 @@ type
|
||||
function ValidWorksheetName(var AName: String;
|
||||
ReplaceDuplicateName: Boolean = false): Boolean;
|
||||
|
||||
{ String-to-cell/range conversion }
|
||||
function TryStrToCell(AText: String; out AWorksheet: TsWorksheet;
|
||||
out ARow,ACol: Cardinal; AListSeparator: Char = #0): Boolean;
|
||||
function TryStrToCellRange(AText: String; out AWorksheet: TsWorksheet;
|
||||
out ARange: TsCellRange; AListSeparator: Char = #0): Boolean;
|
||||
function TryStrToCellRanges(AText: String; out AWorksheet: TsWorksheet;
|
||||
out ARanges: TsCellRangeArray; AListSeparator: Char = #0): Boolean;
|
||||
|
||||
{ Font handling }
|
||||
function AddFont(const AFontName: String; ASize: Single;
|
||||
AStyle: TsFontStyles; AColor: TsColor): Integer; overload;
|
||||
@ -1093,8 +1103,12 @@ type
|
||||
property OnChangeWorksheet: TsWorksheetEvent read FOnChangeWorksheet write FOnChangeWorksheet;
|
||||
{@@ This event fires whenever a workbook is loaded }
|
||||
property OnOpenWorkbook: TNotifyEvent read FOnOpenWorkbook write FOnOpenWorkbook;
|
||||
{@@ This event fires when a worksheet is deleted }
|
||||
{@@ This event fires whenever a worksheet is renamed }
|
||||
property OnRenameWorksheet: TsWorksheetEvent read FOnRenameWorksheet write FOnRenameWorksheet;
|
||||
{@@ This event fires AFTER a worksheet has been deleted }
|
||||
property OnRemoveWorksheet: TsRemoveWorksheetEvent read FOnRemoveWorksheet write FOnRemoveWorksheet;
|
||||
{@@ This event fires BEFORE a worksheet is deleted }
|
||||
property OnRemovingWorksheet: TsWorksheetEvent read FOnRemovingWorksheet write FOnRemovingWorksheet;
|
||||
{@@ This event fires when a worksheet is made "active"}
|
||||
property OnSelectWorksheet: TsWorksheetEvent read FOnSelectWorksheet write FOnSelectWorksheet;
|
||||
{@@ This event allows to provide external cell data for writing to file,
|
||||
@ -3597,7 +3611,7 @@ begin
|
||||
begin
|
||||
FName := AName;
|
||||
if (FWorkbook.FLockCount = 0) and Assigned(FWorkbook.FOnChangeWorksheet) then
|
||||
FWorkbook.FOnChangeWorksheet(FWorkbook, self);
|
||||
FWorkbook.FOnRenameWorksheet(FWorkbook, self);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -7098,6 +7112,8 @@ begin
|
||||
i := GetWorksheetIndex(AWorksheet);
|
||||
if (i <> -1) and (AWorksheet <> nil) then
|
||||
begin
|
||||
if Assigned(FOnRemovingWorksheet) then
|
||||
FOnRemovingWorksheet(self, AWorksheet);
|
||||
FWorksheets.Delete(i);
|
||||
AWorksheet.Free;
|
||||
if Assigned(FOnRemoveWorksheet) then
|
||||
@ -7199,6 +7215,150 @@ begin
|
||||
end;
|
||||
*)
|
||||
|
||||
{ String-to-cell/range conversion }
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Analyses a string which can contain an array of cell ranges along with a
|
||||
worksheet name. Extracts the worksheet (if missing the "active" worksheet of
|
||||
the workbook is returned) and the cell's row and column indexes.
|
||||
|
||||
@param AText General cell range string in Excel notation,
|
||||
i.e. worksheet name + ! + cell in A1 notation.
|
||||
Example: Sheet1!A1:A10; A1:A10 or A1 are valid as well.
|
||||
@param AWorksheet Pointer to the worksheet referred to by AText. If AText
|
||||
does not contain the worksheet name, the active worksheet
|
||||
of the workbook is returned
|
||||
@param ARow, ACol Zero-based row and column index of the cell identified
|
||||
by ATest. If AText contains one ore more cell ranges
|
||||
then the upper left corner of the first range is returned.
|
||||
@param AListSeparator Character to separate the cell blocks in the text
|
||||
If #0 then the ListSeparator of the workbook's FormatSettings
|
||||
is used.
|
||||
@returns TRUE if AText is a valid list of cell ranges, FALSE if not. If the
|
||||
result is FALSE then AWorksheet, ARow and ACol may have unpredictable
|
||||
values.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbook.TryStrToCell(AText: String; out AWorksheet: TsWorksheet;
|
||||
out ARow,ACol: Cardinal; AListSeparator: Char = #0): Boolean;
|
||||
var
|
||||
ranges: TsCellRangeArray;
|
||||
begin
|
||||
Result := TryStrToCellRanges(AText, AWorksheet, ranges, AListSeparator);
|
||||
if Result then
|
||||
begin
|
||||
ARow := ranges[0].Row1;
|
||||
ACol := ranges[0].Col1;
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Analyses a string which can contain an array of cell ranges along with a
|
||||
worksheet name. Extracts the worksheet (if missing the "active" worksheet of
|
||||
the workbook is returned) and the cell range (or the first cell range, if there
|
||||
are several ranges).
|
||||
|
||||
@param AText General cell range string in Excel notation,
|
||||
i.e. worksheet name + ! + cell in A1 notation.
|
||||
Example: Sheet1!A1:A10; A1:A10 or A1 are valid as well.
|
||||
@param AWorksheet Pointer to the worksheet referred to by AText. If AText
|
||||
does not contain the worksheet name, the active worksheet
|
||||
of the workbook is returned
|
||||
@param ARange TsCellRange records identifying the cell block. If AText
|
||||
contains several cell ranges the first one is returned.
|
||||
@param AListSeparator Character to separate the cell blocks in the text
|
||||
If #0 then the ListSeparator of the workbook's FormatSettings
|
||||
is used.
|
||||
@returns TRUE if AText is a valid cell range, FALSE if not. If the
|
||||
result is FALSE then AWorksheet and ARange may have unpredictable
|
||||
values.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbook.TryStrToCellRange(AText: String; out AWorksheet: TsWorksheet;
|
||||
out ARange: TsCellRange; AListSeparator: Char = #0): Boolean;
|
||||
var
|
||||
ranges: TsCellRangeArray;
|
||||
begin
|
||||
Result := TryStrToCellRanges(AText, AWorksheet, ranges, AListSeparator);
|
||||
if Result then ARange := ranges[0];
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Analyses a string which can contain an array of cell ranges along with a
|
||||
worksheet name. Extracts the worksheet (if missing the "active" worksheet of
|
||||
the workbook is returned) and the range array.
|
||||
|
||||
@param AText General cell range string in Excel notation,
|
||||
i.e. worksheet name + ! + cell in A1 notation.
|
||||
Example: Sheet1!A1:A10; A1:A10 or A1 are valid as well.
|
||||
@param AWorksheet Pointer to the worksheet referred to by AText. If AText
|
||||
does not contain the worksheet name, the active worksheet
|
||||
of the workbook is returned
|
||||
@param ARanges Array of TsCellRange records identifying the cell blocks
|
||||
@param AListSeparator Character to separate the cell blocks in the text
|
||||
If #0 then the ListSeparator of the workbook's FormatSettings
|
||||
is used.
|
||||
@returns TRUE if AText is a valid list of cell ranges, FALSE if not. If the
|
||||
result is FALSE then AWorksheet and ARanges may have unpredictable
|
||||
values.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbook.TryStrToCellRanges(AText: String; out AWorksheet: TsWorksheet;
|
||||
out ARanges: TsCellRangeArray; AListSeparator: Char = #0): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
s: String;
|
||||
L: TStrings;
|
||||
begin
|
||||
Result := false;
|
||||
AWorksheet := nil;
|
||||
SetLength(ARanges, 0);
|
||||
|
||||
if AText = '' then
|
||||
exit;
|
||||
|
||||
i := pos(SHEETSEPARATOR, AText);
|
||||
if i = 0 then
|
||||
AWorksheet := FActiveWorksheet
|
||||
else begin
|
||||
AWorksheet := GetWorksheetByName(Copy(AText, 1, i-1));
|
||||
if AWorksheet = nil then
|
||||
exit;
|
||||
AText := Copy(AText, i+1, Length(AText));
|
||||
end;
|
||||
|
||||
L := TStringList.Create;
|
||||
try
|
||||
if AListSeparator = #0 then
|
||||
L.Delimiter := FormatSettings.ListSeparator
|
||||
else
|
||||
L.Delimiter := AListSeparator;
|
||||
L.StrictDelimiter := true;
|
||||
L.DelimitedText := AText;
|
||||
if L.Count = 0 then
|
||||
begin
|
||||
AWorksheet := nil;
|
||||
exit;
|
||||
end;
|
||||
SetLength(ARanges, L.Count);
|
||||
for i:=0 to L.Count-1 do begin
|
||||
if pos(':', L[i]) = 0 then begin
|
||||
Result := ParseCellString(L[i], ARanges[i].Row1, ARanges[i].Col1);
|
||||
if Result then begin
|
||||
ARanges[i].Row2 := ARanges[i].Row1;
|
||||
ARanges[i].Col2 := ARanges[i].Col1;
|
||||
end;
|
||||
end else
|
||||
Result := ParseCellRangeString(L[i], ARanges[i]);
|
||||
if not Result then begin
|
||||
SetLength(ARanges, 0);
|
||||
AWorksheet := nil;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ Font handling }
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
|
@ -1,11 +1,15 @@
|
||||
{
|
||||
fpspreadsheetgrid.pas
|
||||
{ fpspreadsheetchart.pas }
|
||||
|
||||
Chart data source designed to work together with TChart from Lazarus to display the data
|
||||
and with TsWorksheetGrid from FPSpreadsheet to load data from a grid.
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Chart data source designed to work together with TChart from Lazarus
|
||||
to display the data and with FPSpreadsheet to load data.
|
||||
|
||||
AUTHORS: Felipe Monteiro de Carvalho, Werner Pamler
|
||||
|
||||
LICENSE: See the file COPYING.modifiedLGPL.txt, included in the Lazarus
|
||||
distribution, for details about the license.
|
||||
-------------------------------------------------------------------------------}
|
||||
|
||||
AUTHORS: Felipe Monteiro de Carvalho
|
||||
}
|
||||
unit fpspreadsheetchart;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
@ -15,11 +19,11 @@ interface
|
||||
uses
|
||||
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
|
||||
// TChart
|
||||
{tasources,} TACustomSource,
|
||||
// FPSpreadsheet Visual
|
||||
fpspreadsheetctrls, fpspreadsheetgrid,
|
||||
TACustomSource,
|
||||
// FPSpreadsheet
|
||||
fpspreadsheet, fpsutils;
|
||||
fpspreadsheet, fpsutils,
|
||||
// FPSpreadsheet Visual
|
||||
fpspreadsheetctrls, fpspreadsheetgrid;
|
||||
|
||||
type
|
||||
|
||||
@ -83,8 +87,8 @@ type
|
||||
FWorkbookSource: TsWorkbookSource;
|
||||
FWorkbook: TsWorkbook;
|
||||
FWorksheets: array[TsXYRange] of TsWorksheet;
|
||||
FRangeStr: array[TsXYRange] of String;
|
||||
FRanges: array[TsXYRange] of TsCellRangeArray;
|
||||
FDirections: array[TsXYRange] of TsSelectionDirection;
|
||||
FPointsNumber: Cardinal;
|
||||
function GetRange(AIndex: TsXYRange): String;
|
||||
function GetWorkbook: TsWorkbook;
|
||||
@ -94,10 +98,13 @@ type
|
||||
procedure SetWorkbookSource(AValue: TsWorkbookSource);
|
||||
protected
|
||||
FCurItem: TChartDataItem;
|
||||
function BuildRangeStr(AIndex: TsXYRange; AListSeparator: char = #0): String;
|
||||
function CountValues(AIndex: TsXYRange): Integer;
|
||||
function GetCount: Integer; override;
|
||||
function GetItem(AIndex: Integer): PChartDataItem; override;
|
||||
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
||||
procedure Prepare; overload;
|
||||
procedure Prepare(AIndex: TsXYRange); overload;
|
||||
procedure SetYCount(AValue: Cardinal); override;
|
||||
public
|
||||
destructor Destroy; override;
|
||||
@ -270,27 +277,65 @@ end;
|
||||
{ TsWorkbookChartSource }
|
||||
{------------------------------------------------------------------------------}
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Destructor of the WorkbookChartSource.
|
||||
Removes itself from the WorkbookSource's listener list.
|
||||
-------------------------------------------------------------------------------}
|
||||
destructor TsWorkbookChartSource.Destroy;
|
||||
begin
|
||||
if FWorkbookSource <> nil then FWorkbookSource.RemoveListener(self);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Constructs the range string from the stored internal information. Is needed
|
||||
to have the worksheet name in the range string in order to make the range
|
||||
string unique.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbookChartSource.BuildRangeStr(AIndex: TsXYRange;
|
||||
AListSeparator: Char = #0): String;
|
||||
var
|
||||
L: TStrings;
|
||||
range: TsCellRange;
|
||||
begin
|
||||
if (FWorkbook = nil) or (FWorksheets[AIndex] = nil) or (Length(FRanges) = 0) then
|
||||
exit('');
|
||||
|
||||
L := TStringList.Create;
|
||||
try
|
||||
if AListSeparator = #0 then
|
||||
L.Delimiter := FWorkbook.FormatSettings.ListSeparator
|
||||
else
|
||||
L.Delimiter := AListSeparator;
|
||||
L.StrictDelimiter := true;
|
||||
for range in FRanges[AIndex] do
|
||||
L.Add(GetCellRangeString(range, rfAllRel, true));
|
||||
Result := FWorksheets[AIndex].Name + SHEETSEPARATOR + L.DelimitedText;
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Counts the number of x or y values contained in the x/y ranges
|
||||
|
||||
@param AIndex Identifies whether values in the x or y ranges are counted.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbookChartSource.CountValues(AIndex: TsXYRange): Integer;
|
||||
var
|
||||
ir: Integer;
|
||||
range: TsCellRange;
|
||||
begin
|
||||
Result := 0;
|
||||
case FDirections[AIndex] of
|
||||
fpsVerticalSelection:
|
||||
for ir:=0 to High(FRanges[AIndex]) do
|
||||
inc(Result, FRanges[AIndex, ir].Row2 - FRanges[AIndex, ir].Row1 + 1);
|
||||
fpsHorizontalSelection:
|
||||
for ir:=0 to High(FRanges[AIndex]) do
|
||||
inc(Result, FRanges[AIndex, ir].Col2 - FRanges[AIndex, ir].Col1 + 1);
|
||||
for range in FRanges[AIndex] do
|
||||
begin
|
||||
if range.Col1 = range.Col2 then
|
||||
inc(Result, range.Row2 - range.Row1 + 1)
|
||||
else
|
||||
if range.Row1 = range.Row2 then
|
||||
inc(Result, range.Col2 - range.Col1 + 1)
|
||||
else
|
||||
raise Exception.Create('x/y ranges can only be 1 column wide or 1 row high.');
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -306,6 +351,10 @@ end;
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Main ChartSource method called from the series requiring data for plotting.
|
||||
Retrieves the data from the workbook.
|
||||
|
||||
@param AIndex Index of the data point in the series.
|
||||
@return Pointer to a TChartDataItem record containing the x and y coordinates,
|
||||
the data point mark text, and the individual data point color.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbookChartSource.GetItem(AIndex: Integer): PChartDataItem;
|
||||
var
|
||||
@ -325,25 +374,8 @@ end;
|
||||
by the workbook's FormatSettings.
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbookChartsource.GetRange(AIndex: TsXYRange): String;
|
||||
var
|
||||
L: TStrings;
|
||||
ir: Integer;
|
||||
begin
|
||||
if FWorksheets[AIndex] = nil then
|
||||
begin
|
||||
Result := '';
|
||||
exit;
|
||||
end;
|
||||
|
||||
L := TStringList.Create;
|
||||
try
|
||||
L.Delimiter := Workbook.FormatSettings.ListSeparator;
|
||||
for ir:=0 to High(FRanges[AIndex]) do
|
||||
L.Add(GetCellRangeString(FRanges[AIndex, ir], rfAllRel, true));
|
||||
Result := FWorksheets[AIndex].Name + SHEETSEPARATOR + L.DelimitedText;
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
Result := FRangeStr[AIndex];
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@ -358,45 +390,59 @@ begin
|
||||
FWorkbook := Result;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Helper method the prepare the information required for the series data point.
|
||||
|
||||
@param XOrY Identifies whether the method retrieves the x or y
|
||||
coordinate.
|
||||
@param APointIndex Index of the data point for which the data are required
|
||||
@param ANumber (output) x or y coordinate of the data point
|
||||
@param AText Data point marks label text
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbookChartSource.GetXYItem(XOrY:TsXYRange; APointIndex: Integer;
|
||||
out ANumber: Double; out AText: String);
|
||||
var
|
||||
range: TsCellRange;
|
||||
i, j: Integer;
|
||||
idx, ir: Integer;
|
||||
len: Integer;
|
||||
row, col: Cardinal;
|
||||
cell: PCell;
|
||||
begin
|
||||
cell := nil;
|
||||
i := 0;
|
||||
case FDirections[XOrY] of
|
||||
fpsVerticalSelection:
|
||||
for j:=0 to High(FRanges[XOrY]) do begin
|
||||
range := FRanges[XOrY, j];
|
||||
len := range.Row2 - range.Row1 + 1;
|
||||
if (APointIndex >= i) and (APointIndex < i + len) then begin
|
||||
row := range.Row1 + APointIndex - i;
|
||||
col := range.Col1;
|
||||
cell := FWorksheets[XOrY].FindCell(row, col);
|
||||
break;
|
||||
end;
|
||||
inc(i, len);
|
||||
end;
|
||||
idx := 0;
|
||||
if FRanges[XOrY] = nil then
|
||||
exit;
|
||||
|
||||
fpsHorizontalSelection:
|
||||
for j:=0 to High(FRanges[XOrY]) do begin
|
||||
range := FRanges[XOrY, j];
|
||||
len := range.Col2 - range.Col1 + 1;
|
||||
if (APointIndex >= i) and (APointIndex < i + len) then begin
|
||||
row := range.Row1;
|
||||
col := range.Col1 + APointIndex - i;
|
||||
cell := FWorksheets[XOrY].FindCell(row, col);
|
||||
break;
|
||||
end;
|
||||
inc(i, len);
|
||||
for range in FRanges[XOrY] do
|
||||
begin
|
||||
if (range.Col1 = range.Col2) then // vertical range
|
||||
begin
|
||||
len := range.Row2 - range.Row1 + 1;
|
||||
if (APointIndex >= idx) and (APointIndex < idx + len) then
|
||||
begin
|
||||
row := range.Row1 + APointIndex - idx;
|
||||
col := range.Col1;
|
||||
break;
|
||||
end;
|
||||
inc(idx, len);
|
||||
end else // horizontal range
|
||||
if (range.Row1 = range.Row2) then
|
||||
begin
|
||||
len := range.Col2 - range.Col1 + 1;
|
||||
if (APointIndex >= idx) and (APointIndex < idx + len) then
|
||||
begin
|
||||
row := range.Row1;
|
||||
col := range.Col1 + APointIndex - idx;
|
||||
break;
|
||||
end;
|
||||
end else
|
||||
raise Exception.Create('x/y ranges can only be 1 column wide or 1 row high');
|
||||
end;
|
||||
if cell = nil then begin
|
||||
|
||||
cell := FWorksheets[XOrY].FindCell(row, col);
|
||||
|
||||
if cell = nil then
|
||||
begin
|
||||
ANumber := NaN;
|
||||
AText := '';
|
||||
end else
|
||||
@ -417,9 +463,11 @@ end;
|
||||
and to worksheet changes by selecting the tab corresponding to the selected
|
||||
worksheet.
|
||||
|
||||
@param AChangedItems Set with elements identifying whether workbook, worksheet
|
||||
cell content or cell formatting has changed
|
||||
@param AChangedItems Set with elements identifying whether workbook,
|
||||
worksheet, cell content or cell formatting has changed
|
||||
@param AData Additional data, not used here
|
||||
|
||||
@see TsNotificationItem
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbookChartSource.ListenerNotification(
|
||||
AChangedItems: TsNotificationItems; AData: Pointer = nil);
|
||||
@ -431,9 +479,26 @@ var
|
||||
begin
|
||||
Unused(AData);
|
||||
|
||||
// Worksheet changes
|
||||
if (lniWorksheet in AChangedItems) and (Workbook <> nil) then
|
||||
Reset;
|
||||
// Workbook has been successfully loaded, all sheets are ready
|
||||
if (lniWorkbook in AChangedItems) then
|
||||
Prepare;
|
||||
|
||||
// Used worksheet has been renamed?
|
||||
if (lniWorksheetRename in AChangedItems) then
|
||||
for xy in TsXYRange do
|
||||
if TsWorksheet(AData) = FWorksheets[xy] then begin
|
||||
FRangeStr[xy] := BuildRangeStr(xy);
|
||||
Prepare(xy);
|
||||
end;
|
||||
|
||||
// Used worksheet will be deleted?
|
||||
if (lniWorksheetRemoving in AChangedItems) then
|
||||
for xy in TsXYRange do
|
||||
if TsWorksheet(AData) = FWorksheets[xy] then begin
|
||||
FWorksheets[xy] := nil;
|
||||
FRangeStr[xy] := BuildRangeStr(xy);
|
||||
Prepare(xy);
|
||||
end;
|
||||
|
||||
// Cell changes: Enforce recalculation of axes if modified cell is within the
|
||||
// x or y range(s).
|
||||
@ -457,6 +522,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Standard component notification: The ChartSource is notified that the
|
||||
WorkbookSource is being removed.
|
||||
@ -469,6 +535,57 @@ begin
|
||||
SetWorkbookSource(nil);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Parses the x and y cell range strings and extracts internal information
|
||||
(worksheet used, cell range coordinates)
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbookChartSource.Prepare;
|
||||
begin
|
||||
Prepare(rngX);
|
||||
Prepare(rngY);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Parses the range string of the data specified by AIndex and extracts internal
|
||||
information (worksheet used, cell range coordinates)
|
||||
|
||||
@param AIndex Identifies whether x or y cell ranges are analyzed
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbookChartSource.Prepare(AIndex: TsXYRange);
|
||||
const
|
||||
XY: array[TsXYRange] of string = ('x', 'y');
|
||||
var
|
||||
range: TsCellRange;
|
||||
begin
|
||||
if (FWorkbook = nil) or (FRangeStr[AIndex] = '') then begin
|
||||
FWorksheets[AIndex] := nil;
|
||||
SetLength(FRanges[AIndex], 0);
|
||||
FPointsNumber := 0;
|
||||
Reset;
|
||||
exit;
|
||||
end;
|
||||
|
||||
if FWorkbook.TryStrToCellRanges(FRangeStr[AIndex], FWorksheets[AIndex], FRanges[AIndex])
|
||||
then begin
|
||||
for range in FRanges[AIndex] do
|
||||
if (range.Col1 <> range.Col2) and (range.Row1 <> range.Row2) then
|
||||
raise Exception.Create('x/y ranges can only be 1 column wide or 1 row high');
|
||||
FPointsNumber := Max(CountValues(rngX), CountValues(rngY));
|
||||
// If x and y ranges are of different size empty data points will be plotted.
|
||||
Reset;
|
||||
// Make sure to include worksheet name in RangeString.
|
||||
FRangeStr[AIndex] := BuildRangeStr(AIndex);
|
||||
end else
|
||||
if (FWorkbook.GetWorksheetCount > 0) then begin
|
||||
if FWorksheets[AIndex] = nil then
|
||||
raise Exception.CreateFmt('Worksheet of %s cell range "%s" does not exist.',
|
||||
[XY[AIndex], FRangeStr[AIndex]])
|
||||
else
|
||||
raise Exception.CreateFmt('No valid %s cell range in "%s".',
|
||||
[XY[AIndex], FRangeStr[AIndex]]);
|
||||
end;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Resets internal buffers and notfies chart elements of the changes,
|
||||
in particular, enforces recalculation of axis limits
|
||||
@ -491,59 +608,9 @@ end;
|
||||
character defined in the Workbook's FormatSettings.
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbookChartSource.SetRange(AIndex: TsXYRange; const AValue: String);
|
||||
var
|
||||
s: String;
|
||||
p, i: Integer;
|
||||
L: TStrings;
|
||||
sd: TsSelectionDirection;
|
||||
sd0: TsSelectionDirection;
|
||||
begin
|
||||
if (FWorkbook = nil) then
|
||||
exit;
|
||||
|
||||
p := pos(SHEETSEPARATOR, AValue);
|
||||
if p = 0 then
|
||||
begin
|
||||
FWorksheets[AIndex] := FWorkbook.ActiveWorksheet;
|
||||
s := AValue;
|
||||
end else
|
||||
begin
|
||||
s := Copy(AValue, 1, p-1);
|
||||
FWorksheets[AIndex] := FWorkbook.GetWorksheetByName(s);
|
||||
if FWorksheets[AIndex] = nil then
|
||||
raise Exception.CreateFmt('%s cell range "%s" is in a non-existing '+
|
||||
'worksheet.', [''+char(ord('x')+ord(AIndex)), AValue]);
|
||||
s := Copy(AValue, p+1, Length(AValue));
|
||||
end;
|
||||
L := TStringList.Create;
|
||||
try
|
||||
L.Delimiter := FWorkbook.FormatSettings.ListSeparator;
|
||||
L.DelimitedText := s;
|
||||
if L.Count = 0 then
|
||||
raise Exception.CreateFmt('No %s cell range contained in "%s".',
|
||||
[''+char(ord('x')+ord(AIndex)), AValue]
|
||||
);
|
||||
sd := fpsVerticalSelection;
|
||||
SetLength(FRanges[AIndex], L.Count);
|
||||
for i:=0 to L.Count-1 do
|
||||
if ParseCellRangeString(L[i], FRanges[AIndex, i]) then begin
|
||||
if FRanges[AIndex, i].Col1 = FRanges[AIndex, i].Col2 then
|
||||
sd := fpsVerticalSelection
|
||||
else
|
||||
if FRanges[AIndex, i].Row1 = FRanges[AIndex, i].Row2 then
|
||||
sd := fpsHorizontalSelection
|
||||
else
|
||||
raise Exception.Create('Selection can only be 1 column wide or 1 row high');
|
||||
end else
|
||||
raise Exception.CreateFmt('No valid %s cell range in "%s".',
|
||||
[''+char(ord('x')+ord(AIndex)), L[i]]
|
||||
);
|
||||
FPointsNumber := Max(CountValues(rngX), CountValues(rngY));
|
||||
// If x and y ranges are of different size empty data points will be plotted.
|
||||
Reset;
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
FRangeStr[AIndex] := AValue;
|
||||
Prepare;
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@ -564,12 +631,11 @@ end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Inherited ChartSource method telling the series how many y values are used.
|
||||
Currently we support only single valued data
|
||||
Currently we support only single valued data (YCount = 1).
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbookChartSource.SetYCount(AValue: Cardinal);
|
||||
begin
|
||||
FYCount := AValue;
|
||||
// currently not used
|
||||
end;
|
||||
|
||||
|
||||
|
@ -39,8 +39,10 @@ type
|
||||
{@@ Describes during communication between WorkbookSource and visual controls
|
||||
which kind of item has changed: the workbook, the worksheet, a cell value,
|
||||
or a cell formatting, etc. }
|
||||
TsNotificationItem = (lniWorkbook, lniWorksheet, lniCell, lniSelection,
|
||||
lniAbortSelection, lniRow, lniPalette);
|
||||
TsNotificationItem = (lniWorkbook,
|
||||
lniWorksheet, lniWorksheetAdd, lniWorksheetRemoving, lniWorksheetRemove,
|
||||
lniWorksheetRename,
|
||||
lniCell, lniSelection, lniAbortSelection, lniRow, lniPalette);
|
||||
{@@ This set accompanies the notification between WorkbookSource and visual
|
||||
controls and describes which items have changed in the spreadsheet. }
|
||||
TsNotificationItems = set of TsNotificationItem;
|
||||
@ -81,6 +83,8 @@ type
|
||||
procedure WorksheetAddedHandler(Sender: TObject; ASheet: TsWorksheet);
|
||||
procedure WorksheetChangedHandler(Sender: TObject; ASheet: TsWorksheet);
|
||||
procedure WorksheetRemovedHandler(Sender: TObject; ASheetIndex: Integer);
|
||||
procedure WorksheetRemovingHandler(Sender: TObject; AWorksheet: TsWorksheet);
|
||||
procedure WorksheetRenamedHandler(Sender: TObject; AWorksheet: TsWorksheet);
|
||||
procedure WorksheetSelectedHandler(Sender: TObject; AWorksheet: TsWorksheet);
|
||||
|
||||
protected
|
||||
@ -772,6 +776,8 @@ begin
|
||||
FWorkbook.OnAddWorksheet := @WorksheetAddedHandler;
|
||||
FWorkbook.OnChangeWorksheet := @WorksheetChangedHandler;
|
||||
FWorkbook.OnRemoveWorksheet := @WorksheetRemovedHandler;
|
||||
FWorkbook.OnRemovingWorksheet := @WorksheetRemovingHandler;
|
||||
FWorkbook.OnRenameWorksheet := @WorksheetRenamedHandler;
|
||||
FWorkbook.OnSelectWorksheet := @WorksheetSelectedHandler;
|
||||
FWorkbook.OnChangePalette := @WorkbookChangedPaletteHandler;
|
||||
// Pass options to workbook
|
||||
@ -1227,7 +1233,7 @@ procedure TsWorkbookSource.WorksheetAddedHandler(Sender: TObject;
|
||||
ASheet: TsWorksheet);
|
||||
begin
|
||||
Unused(Sender);
|
||||
NotifyListeners([lniWorkbook]);
|
||||
NotifyListeners([lniWorksheetAdd]);
|
||||
SelectWorksheet(ASheet);
|
||||
end;
|
||||
|
||||
@ -1275,10 +1281,34 @@ begin
|
||||
end else
|
||||
sheet := FWorksheet;
|
||||
FWorksheet := sheet; // is needed by listeners!
|
||||
NotifyListeners([lniWorkbook]);
|
||||
NotifyListeners([lniWorksheetRemove]);
|
||||
SelectWorksheet(sheet);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Event handler called BEFORE a worksheet is deleted.
|
||||
|
||||
@param Sender Workbook containing the worksheet
|
||||
@param AWorksheet Worksheet which is to be deleted
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbookSource.WorksheetRemovingHandler(Sender: TObject;
|
||||
AWorksheet: TsWorksheet);
|
||||
begin
|
||||
NotifyListeners([lniWorksheetRemoving], AWorksheet);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Event handler called after a worksheet has been renamed
|
||||
|
||||
@param Sender Workbook containing the worksheet
|
||||
@param AWorksheet Worksheet which has been renamed
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbookSource.WorksheetRenamedHandler(Sender: TObject;
|
||||
AWorksheet: TsWorksheet);
|
||||
begin
|
||||
NotifyListeners([lniWorksheetRename], AWorksheet);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
Event handler called whenever a the workbook makes a worksheet "active".
|
||||
|
||||
@ -1388,16 +1418,23 @@ var
|
||||
begin
|
||||
Unused(AData);
|
||||
|
||||
// Workbook changed
|
||||
if (lniWorkbook in AChangedItems) then
|
||||
// Workbook changed: new workbook, worksheet added/renamed/deleted
|
||||
if (AChangedItems * [lniWorkbook, lniWorksheetAdd, lniWorksheetRemove, lniWorksheetRename] <> []) then
|
||||
begin
|
||||
inc(FLockCount); // avoid WorkbookSelect message when adding each tab
|
||||
GetSheetList(Tabs);
|
||||
TabIndex := Tabs.Count-1;
|
||||
if (lniWorkbook in AChangedItems) then
|
||||
TabIndex := 0
|
||||
else
|
||||
if (lniWorksheetAdd in AChangedItems) then
|
||||
TabIndex := Tabs.Count-1
|
||||
else
|
||||
if (lniWorksheetRename in AChangedItems) then
|
||||
TabIndex := Workbook.GetWorksheetIndex(TsWorksheet(AData));
|
||||
dec(FLockCount);
|
||||
end;
|
||||
|
||||
// Worksheet changed
|
||||
// Worksheet selected
|
||||
if (lniWorksheet in AChangedItems) and (Worksheet <> nil) then
|
||||
begin
|
||||
i := Tabs.IndexOf(Worksheet.Name);
|
||||
|
@ -2902,7 +2902,8 @@ end;
|
||||
procedure TsCustomWorksheetGrid.Loaded;
|
||||
begin
|
||||
inherited;
|
||||
NewWorkbook(FInitColCount, FInitRowCount);
|
||||
if FWorkbookSource = nil then
|
||||
NewWorkbook(FInitColCount, FInitRowCount);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
|
Reference in New Issue
Block a user