
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6461 8e941d3f-bd1b-0410-a28a-d453659cc2b4
677 lines
22 KiB
ObjectPascal
677 lines
22 KiB
ObjectPascal
{ fpspreadsheetchart.pas }
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
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.
|
|
-------------------------------------------------------------------------------}
|
|
|
|
unit fpspreadsheetchart;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
|
|
// TChart
|
|
TACustomSource,
|
|
// FPSpreadsheet
|
|
fpstypes, fpspreadsheet, fpsutils,
|
|
// FPSpreadsheet Visual
|
|
fpspreadsheetctrls, fpspreadsheetgrid;
|
|
|
|
type
|
|
|
|
{@@ Chart data source designed to work together with TChart from Lazarus
|
|
to display the data.
|
|
|
|
The data can be loaded from a TsWorksheetGrid Grid component or
|
|
directly from a TsWorksheet FPSpreadsheet Worksheet }
|
|
|
|
{ TsWorksheetChartSource }
|
|
|
|
{ DEPRECTATED - use TsWorkbookChartSource instead! }
|
|
|
|
TsWorksheetChartSource = class(TCustomChartSource)
|
|
private
|
|
FInternalWorksheet: TsWorksheet;
|
|
FPointsNumber: Integer;
|
|
FXSelectionDirection: TsSelectionDirection;
|
|
FYSelectionDirection: TsSelectionDirection;
|
|
FXFirstCellCol: Cardinal;
|
|
FXFirstCellRow: Cardinal;
|
|
FYFirstCellCol: Cardinal;
|
|
FYFirstCellRow: Cardinal;
|
|
procedure SetPointsNumber(const AValue: Integer);
|
|
procedure SetXSelectionDirection(const AValue: TsSelectionDirection);
|
|
procedure SetYSelectionDirection(const AValue: TsSelectionDirection);
|
|
procedure SetXFirstCellCol(const AValue: Cardinal);
|
|
procedure SetXFirstCellRow(const AValue: Cardinal);
|
|
procedure SetYFirstCellCol(const AValue: Cardinal);
|
|
procedure SetYFirstCellRow(const AValue: Cardinal);
|
|
protected
|
|
FDataWorksheet: TsWorksheet;
|
|
FCurItem: TChartDataItem;
|
|
function GetCount: Integer; override;
|
|
function GetItem(AIndex: Integer): PChartDataItem; override;
|
|
procedure SetYCount(AValue: Cardinal); override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure LoadPropertiesFromStrings(AXInterval, AYInterval, AXTitle, AYTitle, ATitle: string);
|
|
public
|
|
procedure LoadFromWorksheetGrid(const AValue: TsWorksheetGrid);
|
|
published
|
|
property PointsNumber: Integer read FPointsNumber write SetPointsNumber default 0;
|
|
property XFirstCellCol: Cardinal read FXFirstCellCol write SetXFirstCellCol default 0;
|
|
property XFirstCellRow: Cardinal read FXFirstCellRow write SetXFirstCellRow default 0;
|
|
property YFirstCellCol: Cardinal read FYFirstCellCol write SetYFirstCellCol default 0;
|
|
property YFirstCellRow: Cardinal read FYFirstCellRow write SetYFirstCellRow default 0;
|
|
property XSelectionDirection: TsSelectionDirection read FXSelectionDirection write SetXSelectionDirection;
|
|
property YSelectionDirection: TsSelectionDirection read FYSelectionDirection write SetYSelectionDirection;
|
|
end deprecated 'Use TsWorkBOOKChartSource instead of TsWorkSHEETChartSource.';
|
|
|
|
|
|
{ TsWorkbookChartSource }
|
|
|
|
TsXYRange = (rngX, rngY);
|
|
|
|
TsWorkbookChartSource = class(TCustomChartSource, IsSpreadsheetControl)
|
|
private
|
|
FWorkbookSource: TsWorkbookSource;
|
|
// FWorkbook: TsWorkbook;
|
|
FWorksheets: array[TsXYRange] of TsWorksheet;
|
|
FRangeStr: array[TsXYRange] of String;
|
|
FRanges: array[TsXYRange] of TsCellRangeArray;
|
|
FPointsNumber: Cardinal;
|
|
function GetRange(AIndex: TsXYRange): String;
|
|
function GetWorkbook: TsWorkbook;
|
|
procedure GetXYItem(XOrY:TsXYRange; APointIndex: Integer;
|
|
out ANumber: Double; out AText: String);
|
|
procedure SetRange(AIndex: TsXYRange; const AValue: String);
|
|
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;
|
|
procedure Reset;
|
|
property PointsNumber: Cardinal read FPointsNumber;
|
|
property Workbook: TsWorkbook read GetWorkbook;
|
|
public
|
|
// Interface to TsWorkbookSource
|
|
procedure ListenerNotification(AChangedItems: TsNotificationItems; AData: Pointer = nil);
|
|
procedure RemoveWorkbookSource;
|
|
published
|
|
property WorkbookSource: TsWorkbookSource read FWorkbookSource write SetWorkbookSource;
|
|
property XRange: String index rngX read GetRange write SetRange;
|
|
property YRange: String index rngY read GetRange write SetRange;
|
|
end;
|
|
|
|
|
|
//procedure Register;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
Math;
|
|
|
|
{
|
|
procedure Register;
|
|
begin
|
|
RegisterComponents('Chart', [TsWorksheetChartSource, TsWorkbookChartSource]);
|
|
end;
|
|
}
|
|
|
|
{ TsWorksheetChartSource }
|
|
|
|
procedure TsWorksheetChartSource.SetPointsNumber(const AValue: Integer);
|
|
begin
|
|
if FPointsNumber = AValue then exit;
|
|
FPointsNumber := AValue;
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.SetXSelectionDirection(
|
|
const AValue: TsSelectionDirection);
|
|
begin
|
|
if FXSelectionDirection=AValue then exit;
|
|
FXSelectionDirection:=AValue;
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.SetYSelectionDirection(
|
|
const AValue: TsSelectionDirection);
|
|
begin
|
|
if FYSelectionDirection=AValue then exit;
|
|
FYSelectionDirection:=AValue;
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.SetXFirstCellCol(const AValue: Cardinal);
|
|
begin
|
|
if FXFirstCellCol=AValue then exit;
|
|
FXFirstCellCol:=AValue;
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.SetXFirstCellRow(const AValue: Cardinal);
|
|
begin
|
|
if FXFirstCellRow=AValue then exit;
|
|
FXFirstCellRow:=AValue;
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.SetYFirstCellCol(const AValue: Cardinal);
|
|
begin
|
|
if FYFirstCellCol=AValue then exit;
|
|
FYFirstCellCol:=AValue;
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.SetYFirstCellRow(const AValue: Cardinal);
|
|
begin
|
|
if FYFirstCellRow=AValue then exit;
|
|
FYFirstCellRow:=AValue;
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
function TsWorksheetChartSource.GetCount: Integer;
|
|
begin
|
|
Result := FPointsNumber;
|
|
end;
|
|
|
|
function TsWorksheetChartSource.GetItem(AIndex: Integer): PChartDataItem;
|
|
var
|
|
XRow, XCol, YRow, YCol: Integer;
|
|
cell: PCell;
|
|
begin
|
|
// First calculate the cell position
|
|
if XSelectionDirection = fpsVerticalSelection then
|
|
begin
|
|
XRow := Integer(FXFirstCellRow) + AIndex;
|
|
XCol := FXFirstCellCol;
|
|
end
|
|
else
|
|
begin
|
|
XRow := FXFirstCellRow;
|
|
XCol := Integer(FXFirstCellCol) + AIndex;
|
|
end;
|
|
|
|
if YSelectionDirection = fpsVerticalSelection then
|
|
begin
|
|
YRow := Integer(FYFirstCellRow) + AIndex;
|
|
YCol := FYFirstCellCol;
|
|
end
|
|
else
|
|
begin
|
|
YRow := FYFirstCellRow;
|
|
YCol := Integer(FYFirstCellCol) + AIndex;
|
|
end;
|
|
|
|
cell := FDataWorksheet.FindCell(XRow, XCol);
|
|
if cell = nil then
|
|
begin
|
|
FCurItem.X := NaN;
|
|
FCurITem.Text := '';
|
|
end else
|
|
if cell^.ContentType = cctUTF8String then begin
|
|
FCurItem.X := AIndex;
|
|
FCurItem.Text := FDataWorksheet.ReadAsText(cell);
|
|
end else
|
|
begin
|
|
FCurItem.X := FDataWorksheet.ReadAsNumber(cell);
|
|
FCurItem.Text := '';
|
|
end;
|
|
|
|
cell := FDataWorksheet.FindCell(YRow, YCol);
|
|
if cell = nil then
|
|
FCurItem.Y := NaN
|
|
else
|
|
FCurItem.Y := FDataWorksheet.ReadAsNumber(cell);
|
|
|
|
FCurItem.Color := clDefault;
|
|
|
|
Result := @FCurItem;
|
|
end;
|
|
|
|
constructor TsWorksheetChartSource.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FInternalWorksheet := TsWorksheet.Create;
|
|
FDataWorksheet := FInternalWorksheet;
|
|
end;
|
|
|
|
destructor TsWorksheetChartSource.Destroy;
|
|
begin
|
|
if FInternalWorksheet <> nil then FInternalWorksheet.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.LoadFromWorksheetGrid(const AValue: TsWorksheetGrid);
|
|
begin
|
|
if AValue = nil then Exit;
|
|
|
|
FDataWorksheet := AValue.Worksheet;
|
|
// AValue.SaveToWorksheet(FDataWorksheet);
|
|
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.LoadPropertiesFromStrings(AXInterval,
|
|
AYInterval, AXTitle, AYTitle, ATitle: string);
|
|
var
|
|
lXCount, lYCount: Cardinal;
|
|
begin
|
|
Unused(AXTitle, AYTitle, ATitle);
|
|
ParseIntervalString(AXInterval, FXFirstCellRow, FXFirstCellCol, lXCount, FXSelectionDirection);
|
|
ParseIntervalString(AYInterval, FYFirstCellRow, FYFirstCellCol, lYCount, FYSelectionDirection);
|
|
if lXCount <> lYCount then raise Exception.Create(
|
|
'TsWorksheetChartSource.LoadPropertiesFromStrings: Interval sizes don''t match');
|
|
FPointsNumber := lXCount;
|
|
end;
|
|
|
|
procedure TsWorksheetChartSource.SetYCount(AValue: Cardinal);
|
|
begin
|
|
FYCount := AValue;
|
|
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 (Workbook = nil) or (FWorksheets[AIndex] = nil) or (Length(FRanges) = 0) then
|
|
exit('');
|
|
|
|
L := TStringList.Create;
|
|
try
|
|
if AListSeparator = #0 then
|
|
L.Delimiter := Workbook.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
|
|
range: TsCellRange;
|
|
begin
|
|
Result := 0;
|
|
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;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Inherited ChartSource method telling the series how many data points are
|
|
available
|
|
-------------------------------------------------------------------------------}
|
|
function TsWorkbookChartSource.GetCount: Integer;
|
|
begin
|
|
Result := FPointsNumber;
|
|
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
|
|
dummy: String;
|
|
begin
|
|
GetXYItem(rngX, AIndex, FCurItem.X, FCurItem.Text);
|
|
GetXYItem(rngY, AIndex, FCurItem.Y, dummy);
|
|
FCurItem.Color := clDefault;
|
|
Result := @FCurItem;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Getter method for the cell range used for x or y coordinates (or x labels)
|
|
|
|
@param AIndex Determines whether the methods deals with x or y values
|
|
@return An Excel string containing workbookname and cell block(s) in A1
|
|
notation. Multiple blocks are separated by the ListSeparator defined
|
|
by the workbook's FormatSettings.
|
|
-------------------------------------------------------------------------------}
|
|
function TsWorkbookChartsource.GetRange(AIndex: TsXYRange): String;
|
|
begin
|
|
Result := FRangeStr[AIndex];
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Getter method for the linked workbook
|
|
-------------------------------------------------------------------------------}
|
|
function TsWorkbookChartSource.GetWorkbook: TsWorkbook;
|
|
begin
|
|
if FWorkbookSource <> nil then
|
|
Result := WorkbookSource.Workbook
|
|
else
|
|
Result := nil;
|
|
// 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;
|
|
idx: Integer;
|
|
len: Integer;
|
|
row, col: Cardinal;
|
|
cell: PCell;
|
|
begin
|
|
cell := nil;
|
|
idx := 0;
|
|
if FRanges[XOrY] = nil then
|
|
exit;
|
|
|
|
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 := longint(range.Row1) + APointIndex - idx;
|
|
col := range.Col1;
|
|
break;
|
|
end;
|
|
inc(idx, len);
|
|
end else // horizontal range
|
|
if (range.Row1 = range.Row2) then
|
|
begin
|
|
len := longint(range.Col2) - range.Col1 + 1;
|
|
if (APointIndex >= idx) and (APointIndex < idx + len) then
|
|
begin
|
|
row := range.Row1;
|
|
col := longint(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;
|
|
|
|
cell := FWorksheets[XOrY].FindCell(row, col);
|
|
|
|
if cell = nil then
|
|
begin
|
|
ANumber := NaN;
|
|
AText := '';
|
|
end else
|
|
if cell^.ContentType = cctUTF8String then begin
|
|
ANumber := APointIndex;
|
|
AText := FWorksheets[rngX].ReadAsText(cell);
|
|
end else
|
|
begin
|
|
ANumber := FWorksheets[rngX].ReadAsNumber(cell);
|
|
AText := '';
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Notification message received from the WorkbookSource telling which
|
|
spreadsheet item has changed.
|
|
Responds to workbook changes by reading the worksheet names into the tabs,
|
|
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 AData Additional data, not used here
|
|
|
|
@see TsNotificationItem
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsWorkbookChartSource.ListenerNotification(
|
|
AChangedItems: TsNotificationItems; AData: Pointer = nil);
|
|
var
|
|
ir: Integer;
|
|
cell: PCell;
|
|
ResetDone: Boolean;
|
|
xy: TsXYRange;
|
|
begin
|
|
Unused(AData);
|
|
|
|
// 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).
|
|
if (lniCell in AChangedItems) and (Workbook <> nil) then
|
|
begin
|
|
cell := PCell(AData);
|
|
if (cell <> nil) then begin
|
|
ResetDone := false;
|
|
for xy in TsXYrange do
|
|
for ir:=0 to High(FRanges[xy]) do
|
|
begin
|
|
if FWorksheets[xy].CellInRange(cell^.Row, cell^.Col, FRanges[xy, ir]) then
|
|
begin
|
|
Reset;
|
|
ResetDone := true;
|
|
break;
|
|
end;
|
|
if ResetDone then break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Standard component notification: The ChartSource is notified that the
|
|
WorkbookSource is being removed.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsWorkbookChartSource.Notification(AComponent: TComponent;
|
|
Operation: TOperation);
|
|
begin
|
|
inherited Notification(AComponent, Operation);
|
|
if (Operation = opRemove) and (AComponent = FWorkbookSource) then
|
|
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 (Workbook = nil) or (FRangeStr[AIndex] = '') //or (FWorksheets[AIndex] = nil)
|
|
then begin
|
|
FWorksheets[AIndex] := nil;
|
|
SetLength(FRanges[AIndex], 0);
|
|
FPointsNumber := 0;
|
|
Reset;
|
|
exit;
|
|
end;
|
|
|
|
if Workbook.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 (Workbook.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;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Removes the link of the ChartSource to the WorkbookSource.
|
|
Required before destruction.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsWorkbookChartSource.RemoveWorkbookSource;
|
|
begin
|
|
SetWorkbookSource(nil);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Resets internal buffers and notfies chart elements of the changes,
|
|
in particular, enforces recalculation of axis limits
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsWorkbookChartSource.Reset;
|
|
begin
|
|
InvalidateCaches;
|
|
Notify;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Setter method for the cell range used for x or y data (or labels) in the chart
|
|
If it does not contain the worksheet name the currently active worksheet of
|
|
the WorkbookSource is assumed.
|
|
|
|
@param AIndex Distinguishes whether the method deals with x or y ranges
|
|
@param AValue String in Excel syntax containing the cell range to be
|
|
used for x or y (depending on AIndex). Can contain multiple
|
|
cell blocks which must be separator by the ListSeparator
|
|
character defined in the Workbook's FormatSettings.
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsWorkbookChartSource.SetRange(AIndex: TsXYRange; const AValue: String);
|
|
begin
|
|
FRangeStr[AIndex] := AValue;
|
|
Prepare;
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Setter method for the WorkbookSource
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsWorkbookChartSource.SetWorkbookSource(AValue: TsWorkbookSource);
|
|
begin
|
|
if AValue = FWorkbookSource then
|
|
exit;
|
|
if FWorkbookSource <> nil then
|
|
FWorkbookSource.RemoveListener(self);
|
|
FWorkbookSource := AValue;
|
|
if FWorkbookSource <> nil then
|
|
FWorkbookSource.AddListener(self);
|
|
// FWorkbook := GetWorkbook;
|
|
ListenerNotification([lniWorkbook, lniWorksheet]);
|
|
end;
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
Inherited ChartSource method telling the series how many y values are used.
|
|
Currently we support only single valued data (YCount = 1, by default).
|
|
-------------------------------------------------------------------------------}
|
|
procedure TsWorkbookChartSource.SetYCount(AValue: Cardinal);
|
|
begin
|
|
FYCount := AValue;
|
|
end;
|
|
|
|
|
|
end.
|