2010-05-01 18:10:38 +00:00
|
|
|
{
|
|
|
|
fpspreadsheetgrid.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.
|
|
|
|
|
|
|
|
AUTHORS: Felipe Monteiro de Carvalho
|
|
|
|
}
|
|
|
|
unit fpspreadsheetchart;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2014-06-19 19:25:40 +00:00
|
|
|
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
|
2010-05-01 18:10:38 +00:00
|
|
|
// TChart
|
2011-01-04 11:04:27 +00:00
|
|
|
{tasources,} TACustomSource,
|
2010-05-01 18:10:38 +00:00
|
|
|
// FPSpreadsheet Visual
|
2015-01-03 20:03:55 +00:00
|
|
|
fpspreadsheetctrls, fpspreadsheetgrid,
|
2010-05-01 18:10:38 +00:00
|
|
|
// FPSpreadsheet
|
2010-05-25 09:11:02 +00:00
|
|
|
fpspreadsheet, fpsutils;
|
2010-05-01 18:10:38 +00:00
|
|
|
|
|
|
|
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 }
|
|
|
|
|
2015-01-03 20:03:55 +00:00
|
|
|
{ DEPRECTATED - use TsWorkbookChartSource instead! }
|
|
|
|
|
2010-05-01 18:10:38 +00:00
|
|
|
TsWorksheetChartSource = class(TCustomChartSource)
|
|
|
|
private
|
|
|
|
FInternalWorksheet: TsWorksheet;
|
|
|
|
FPointsNumber: Integer;
|
|
|
|
FXSelectionDirection: TsSelectionDirection;
|
|
|
|
FYSelectionDirection: TsSelectionDirection;
|
|
|
|
// FWorksheetGrid: TsWorksheetGrid;
|
2014-07-02 19:25:37 +00:00
|
|
|
FXFirstCellCol: Cardinal;
|
|
|
|
FXFirstCellRow: Cardinal;
|
|
|
|
FYFirstCellCol: Cardinal;
|
|
|
|
FYFirstCellRow: Cardinal;
|
2010-05-01 18:10:38 +00:00
|
|
|
procedure SetPointsNumber(const AValue: Integer);
|
|
|
|
procedure SetXSelectionDirection(const AValue: TsSelectionDirection);
|
|
|
|
procedure SetYSelectionDirection(const AValue: TsSelectionDirection);
|
2014-07-02 19:25:37 +00:00
|
|
|
procedure SetXFirstCellCol(const AValue: Cardinal);
|
|
|
|
procedure SetXFirstCellRow(const AValue: Cardinal);
|
|
|
|
procedure SetYFirstCellCol(const AValue: Cardinal);
|
|
|
|
procedure SetYFirstCellRow(const AValue: Cardinal);
|
2010-05-01 18:10:38 +00:00
|
|
|
protected
|
|
|
|
FDataWorksheet: TsWorksheet;
|
|
|
|
FCurItem: TChartDataItem;
|
|
|
|
function GetCount: Integer; override;
|
|
|
|
function GetItem(AIndex: Integer): PChartDataItem; override;
|
|
|
|
public
|
|
|
|
constructor Create(AOwner: TComponent); override;
|
|
|
|
destructor Destroy; override;
|
2010-05-25 09:11:02 +00:00
|
|
|
procedure LoadPropertiesFromStrings(AXInterval, AYInterval, AXTitle, AYTitle, ATitle: string);
|
2010-05-01 18:10:38 +00:00
|
|
|
public
|
2014-06-01 23:06:19 +00:00
|
|
|
procedure LoadFromWorksheetGrid(const AValue: TsWorksheetGrid);
|
2010-05-01 18:10:38 +00:00
|
|
|
published
|
|
|
|
// property WorksheetGrid: TsWorksheetGrid read FWorksheetGrid write SetWorksheetGrid;
|
|
|
|
property PointsNumber: Integer read FPointsNumber write SetPointsNumber default 0;
|
2014-07-02 19:25:37 +00:00
|
|
|
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;
|
2010-05-01 18:10:38 +00:00
|
|
|
property XSelectionDirection: TsSelectionDirection read FXSelectionDirection write SetXSelectionDirection;
|
|
|
|
property YSelectionDirection: TsSelectionDirection read FYSelectionDirection write SetYSelectionDirection;
|
|
|
|
end;
|
|
|
|
|
2015-01-03 20:03:55 +00:00
|
|
|
|
|
|
|
{ TsWorkbookChartSource }
|
|
|
|
|
|
|
|
TsXYRange = (rngX, rngY);
|
|
|
|
|
|
|
|
TsWorkbookChartSource = class(TCustomChartSource)
|
|
|
|
private
|
|
|
|
FWorkbookSource: TsWorkbookSource;
|
|
|
|
FWorkbook: TsWorkbook;
|
|
|
|
FWorksheets: array[TsXYRange] of TsWorksheet;
|
|
|
|
FRanges: array[TsXYRange] of TsCellRangeArray;
|
|
|
|
FDirections: array[TsXYRange] of TsSelectionDirection;
|
|
|
|
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 CountValues(AIndex: TsXYRange): Integer;
|
|
|
|
function GetCount: Integer; override;
|
|
|
|
function GetItem(AIndex: Integer): PChartDataItem; override;
|
|
|
|
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
|
|
|
procedure SetYCount(AValue: Cardinal); override;
|
|
|
|
public
|
|
|
|
destructor Destroy; override;
|
|
|
|
procedure ListenerNotification(AChangedItems: TsNotificationItems; AData: Pointer = nil);
|
|
|
|
procedure Reset;
|
|
|
|
property PointsNumber: Cardinal read FPointsNumber;
|
|
|
|
property Workbook: TsWorkbook read GetWorkbook;
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-05-01 18:10:38 +00:00
|
|
|
procedure Register;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
2015-01-03 20:03:55 +00:00
|
|
|
uses
|
|
|
|
Math;
|
|
|
|
|
|
|
|
|
2010-05-01 18:10:38 +00:00
|
|
|
procedure Register;
|
|
|
|
begin
|
2015-01-03 20:03:55 +00:00
|
|
|
RegisterComponents('Chart', [TsWorksheetChartSource, TsWorkbookChartSource]);
|
2010-05-01 18:10:38 +00:00
|
|
|
end;
|
|
|
|
|
2015-01-03 20:03:55 +00:00
|
|
|
|
2010-05-01 18:10:38 +00:00
|
|
|
{ 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;
|
|
|
|
|
2014-07-02 19:25:37 +00:00
|
|
|
procedure TsWorksheetChartSource.SetXFirstCellCol(const AValue: Cardinal);
|
2010-05-01 18:10:38 +00:00
|
|
|
begin
|
|
|
|
if FXFirstCellCol=AValue then exit;
|
|
|
|
FXFirstCellCol:=AValue;
|
|
|
|
InvalidateCaches;
|
|
|
|
Notify;
|
|
|
|
end;
|
|
|
|
|
2014-07-02 19:25:37 +00:00
|
|
|
procedure TsWorksheetChartSource.SetXFirstCellRow(const AValue: Cardinal);
|
2010-05-01 18:10:38 +00:00
|
|
|
begin
|
|
|
|
if FXFirstCellRow=AValue then exit;
|
|
|
|
FXFirstCellRow:=AValue;
|
|
|
|
InvalidateCaches;
|
|
|
|
Notify;
|
|
|
|
end;
|
|
|
|
|
2014-07-02 19:25:37 +00:00
|
|
|
procedure TsWorksheetChartSource.SetYFirstCellCol(const AValue: Cardinal);
|
2010-05-01 18:10:38 +00:00
|
|
|
begin
|
|
|
|
if FYFirstCellCol=AValue then exit;
|
|
|
|
FYFirstCellCol:=AValue;
|
|
|
|
InvalidateCaches;
|
|
|
|
Notify;
|
|
|
|
end;
|
|
|
|
|
2014-07-02 19:25:37 +00:00
|
|
|
procedure TsWorksheetChartSource.SetYFirstCellRow(const AValue: Cardinal);
|
2010-05-01 18:10:38 +00:00
|
|
|
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;
|
|
|
|
begin
|
|
|
|
// First calculate the cell position
|
|
|
|
if XSelectionDirection = fpsVerticalSelection then
|
|
|
|
begin
|
2014-10-06 10:43:10 +00:00
|
|
|
XRow := Integer(FXFirstCellRow) + AIndex;
|
2010-05-01 18:10:38 +00:00
|
|
|
XCol := FXFirstCellCol;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
XRow := FXFirstCellRow;
|
2014-10-06 10:43:10 +00:00
|
|
|
XCol := Integer(FXFirstCellCol) + AIndex;
|
2010-05-01 18:10:38 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
if YSelectionDirection = fpsVerticalSelection then
|
|
|
|
begin
|
2014-10-06 10:43:10 +00:00
|
|
|
YRow := Integer(FYFirstCellRow) + AIndex;
|
2010-05-01 18:10:38 +00:00
|
|
|
YCol := FYFirstCellCol;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
YRow := FYFirstCellRow;
|
2014-10-06 10:43:10 +00:00
|
|
|
YCol := Integer(FYFirstCellCol) + AIndex;
|
2010-05-01 18:10:38 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
// Check the corresponding cell, if it is empty, use zero
|
|
|
|
// If not, then get a number value
|
|
|
|
|
|
|
|
FCurItem.X := FDataWorksheet.ReadAsNumber(XRow, XCol);
|
|
|
|
FCurItem.Y := FDataWorksheet.ReadAsNumber(YRow, YCol);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2014-06-01 23:06:19 +00:00
|
|
|
procedure TsWorksheetChartSource.LoadFromWorksheetGrid(const AValue: TsWorksheetGrid);
|
|
|
|
begin
|
|
|
|
if AValue = nil then Exit;
|
|
|
|
|
|
|
|
FDataWorksheet := AValue.Worksheet;
|
|
|
|
// AValue.SaveToWorksheet(FDataWorksheet);
|
|
|
|
|
|
|
|
InvalidateCaches;
|
|
|
|
Notify;
|
|
|
|
end;
|
|
|
|
|
2010-05-25 09:11:02 +00:00
|
|
|
procedure TsWorksheetChartSource.LoadPropertiesFromStrings(AXInterval,
|
|
|
|
AYInterval, AXTitle, AYTitle, ATitle: string);
|
|
|
|
var
|
2014-07-02 19:25:37 +00:00
|
|
|
lXCount, lYCount: Cardinal;
|
2010-05-25 09:11:02 +00:00
|
|
|
begin
|
2014-06-20 15:58:22 +00:00
|
|
|
Unused(AXTitle, AYTitle, ATitle);
|
2010-05-25 09:11:02 +00:00
|
|
|
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;
|
|
|
|
|
2015-01-03 20:03:55 +00:00
|
|
|
|
|
|
|
{------------------------------------------------------------------------------}
|
|
|
|
{ TsWorkbookChartSource }
|
|
|
|
{------------------------------------------------------------------------------}
|
|
|
|
|
|
|
|
destructor TsWorkbookChartSource.Destroy;
|
|
|
|
begin
|
|
|
|
if FWorkbookSource <> nil then FWorkbookSource.RemoveListener(self);
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
Counts the number of x or y values contained in the x/y ranges
|
|
|
|
-------------------------------------------------------------------------------}
|
|
|
|
function TsWorkbookChartSource.CountValues(AIndex: TsXYRange): Integer;
|
|
|
|
var
|
|
|
|
ir: Integer;
|
|
|
|
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);
|
|
|
|
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.
|
|
|
|
-------------------------------------------------------------------------------}
|
|
|
|
function TsWorkbookChartSource.GetItem(AIndex: Integer): PChartDataItem;
|
|
|
|
var
|
|
|
|
dummy: String;
|
|
|
|
begin
|
|
|
|
GetXYItem(rngX, AIndex, FCurItem.X, FCurItem.Text);
|
|
|
|
GetXYItem(rngY, AIndex, FCurItem.Y, dummy);
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
|
|
|
|
procedure TsWorkbookChartSource.GetXYItem(XOrY:TsXYRange; APointIndex: Integer;
|
|
|
|
out ANumber: Double; out AText: String);
|
|
|
|
var
|
|
|
|
range: TsCellRange;
|
|
|
|
i, j: 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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if cell = nil then begin
|
|
|
|
ANumber := NaN;
|
|
|
|
AText := '';
|
|
|
|
end else
|
|
|
|
if cell^.ContentType = cctUTF8String then begin
|
|
|
|
ANumber := APointIndex;
|
|
|
|
AText := FWorksheets[rngX].ReadAsUTF8Text(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
|
|
|
|
-------------------------------------------------------------------------------}
|
|
|
|
procedure TsWorkbookChartSource.ListenerNotification(
|
|
|
|
AChangedItems: TsNotificationItems; AData: Pointer = nil);
|
|
|
|
var
|
|
|
|
ir: Integer;
|
|
|
|
cell: PCell;
|
|
|
|
ResetDone: Boolean;
|
|
|
|
xy: TsXYRange;
|
|
|
|
begin
|
|
|
|
Unused(AData);
|
|
|
|
|
|
|
|
// Worksheet changes
|
|
|
|
if (lniWorksheet in AChangedItems) and (Workbook <> nil) then
|
|
|
|
Reset;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
{@@ ----------------------------------------------------------------------------
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
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
|
|
|
|
-------------------------------------------------------------------------------}
|
|
|
|
procedure TsWorkbookChartSource.SetYCount(AValue: Cardinal);
|
|
|
|
begin
|
|
|
|
FYCount := AValue;
|
|
|
|
// currently not used
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2010-05-01 18:10:38 +00:00
|
|
|
end.
|