fpspreadsheet: Extended chart display demo. Rework TsWorkbookSource's ListenerNotification to avoid crash when chart destroy a listening chartsource. Chart link can display pie series now.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9053 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2023-11-28 23:04:27 +00:00
parent 8b51f30e14
commit e041a4109e
7 changed files with 260 additions and 108 deletions

View File

@ -65,6 +65,7 @@
<Linking> <Linking>
<Debugging> <Debugging>
<DebugInfoType Value="dsDwarf3"/> <DebugInfoType Value="dsDwarf3"/>
<UseHeaptrc Value="True"/>
</Debugging> </Debugging>
<Options> <Options>
<Win32> <Win32>

View File

@ -10,8 +10,8 @@ object Form1: TForm1
OnCreate = FormCreate OnCreate = FormCreate
object sWorksheetGrid1: TsWorksheetGrid object sWorksheetGrid1: TsWorksheetGrid
Left = 0 Left = 0
Height = 527 Height = 489
Top = 0 Top = 38
Width = 402 Width = 402
FrozenCols = 0 FrozenCols = 0
FrozenRows = 0 FrozenRows = 0
@ -28,14 +28,14 @@ object Form1: TForm1
end end
object Splitter1: TSplitter object Splitter1: TSplitter
Left = 402 Left = 402
Height = 527 Height = 489
Top = 0 Top = 38
Width = 5 Width = 5
end end
object Chart1: TChart object Chart1: TChart
Left = 407 Left = 407
Height = 527 Height = 489
Top = 0 Top = 38
Width = 694 Width = 694
AxisList = < AxisList = <
item item
@ -55,10 +55,96 @@ object Form1: TForm1
) )
Align = alClient Align = alClient
end end
object Panel1: TPanel
Left = 0
Height = 38
Top = 0
Width = 1101
Align = alTop
AutoSize = True
BevelOuter = bvNone
ClientHeight = 38
ClientWidth = 1101
TabOrder = 3
object Label1: TLabel
AnchorSideLeft.Control = Panel1
AnchorSideTop.Control = Panel1
AnchorSideTop.Side = asrCenter
Left = 6
Height = 15
Top = 12
Width = 51
BorderSpacing.Around = 6
Caption = 'File name'
end
object ComboBox1: TComboBox
AnchorSideLeft.Control = Label1
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = Panel1
AnchorSideTop.Side = asrCenter
AnchorSideRight.Control = Button1
Left = 63
Height = 23
Top = 8
Width = 911
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Around = 6
ItemHeight = 15
Items.Strings = (
'../../../other/chart/area.ods'
'../../../other/chart/bars.ods'
'../../../other/chart/bubble.ods'
'../../../other/chart/pie.ods'
'../../../other/chart/radar.ods'
'../../../other/chart/regression.ods'
)
TabOrder = 0
TextHint = 'Enter or select file name'
OnCloseUp = ComboBox1CloseUp
end
object Button1: TButton
AnchorSideTop.Control = Panel1
AnchorSideTop.Side = asrCenter
AnchorSideRight.Control = Button2
Left = 980
Height = 25
Top = 7
Width = 35
Anchors = [akTop, akRight]
AutoSize = True
BorderSpacing.Around = 6
Caption = '...'
TabOrder = 1
OnClick = Button1Click
end
object Button2: TButton
AnchorSideTop.Control = Panel1
AnchorSideTop.Side = asrCenter
AnchorSideRight.Control = Panel1
AnchorSideRight.Side = asrBottom
Left = 1021
Height = 25
Top = 7
Width = 74
Anchors = [akTop, akRight]
AutoSize = True
BorderSpacing.Around = 6
Caption = 'Open file'
TabOrder = 2
OnClick = Button2Click
end
end
object sWorkbookSource1: TsWorkbookSource object sWorkbookSource1: TsWorkbookSource
FileFormat = sfUser FileFormat = sfUser
Options = [] Options = []
Left = 244 Left = 244
Top = 138 Top = 138
end end
object OpenDialog1: TOpenDialog
DefaultExt = '.ods'
Filter = 'OpenDocument Spreadsheet Files|*.ods'
Options = [ofFileMustExist, ofEnableSizing, ofViewDetail]
Left = 976
Top = 56
end
end end

View File

@ -5,7 +5,7 @@ unit main;
interface interface
uses uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
TAGraph, TAGraph,
fpSpreadsheet, fpsTypes, fpsOpenDocument, fpSpreadsheet, fpsTypes, fpsOpenDocument,
fpSpreadsheetCtrls, fpSpreadsheetGrid, fpSpreadsheetChart; fpSpreadsheetCtrls, fpSpreadsheetGrid, fpSpreadsheetChart;
@ -15,13 +15,24 @@ type
{ TForm1 } { TForm1 }
TForm1 = class(TForm) TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Chart1: TChart; Chart1: TChart;
ComboBox1: TComboBox;
Label1: TLabel;
OpenDialog1: TOpenDialog;
Panel1: TPanel;
Splitter1: TSplitter; Splitter1: TSplitter;
sWorkbookSource1: TsWorkbookSource; sWorkbookSource1: TsWorkbookSource;
sWorksheetGrid1: TsWorksheetGrid; sWorksheetGrid1: TsWorksheetGrid;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure ComboBox1CloseUp(Sender: TObject);
procedure FormCreate(Sender: TObject); procedure FormCreate(Sender: TObject);
private private
sChartLink: TsWorkbookChartLink; sChartLink: TsWorkbookChartLink;
FFileName: String;
procedure LoadFile(AFileName: String);
public public
@ -46,11 +57,47 @@ const
{ TForm1 } { TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
OpenDialog1.InitialDir := ExtractFilePath(Combobox1.Text);
OpenDialog1.FileName := '';
if OpenDialog1.Execute then
begin
Combobox1.Text := OpenDialog1.FileName;
LoadFile(OpenDialog1.FileName);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
LoadFile(Combobox1.Text);
end;
procedure TForm1.ComboBox1CloseUp(Sender: TObject);
begin
Combobox1.Text := Combobox1.Items[Combobox1.ItemIndex];
LoadFile(Combobox1.Text);
end;
procedure TForm1.FormCreate(Sender: TObject); procedure TForm1.FormCreate(Sender: TObject);
begin
if ParamCount > 0 then
begin
Combobox1.Text := ParamStr(1);
LoadFile(Combobox1.Text);
end;
end;
procedure TForm1.LoadFile(AFileName: String);
var var
fn: String; fn: String;
begin begin
fn := ExpandFileName(FILE_NAME); fn := ExpandFileName(AFileName);
if not FileExists(fn) then
begin
MessageDlg('File "' + fn + '" not found.', mtError, [mbOK], 0);
exit;
end;
sWorkbookSource1.FileFormat := sfOpenDocument; sWorkbookSource1.FileFormat := sfOpenDocument;
if FileExists(fn) then if FileExists(fn) then
@ -61,6 +108,5 @@ begin
sChartLink.WorkbookSource := sWorkbookSource1; sChartLink.WorkbookSource := sWorkbookSource1;
sChartLink.WorkbookChartIndex := 0; sChartLink.WorkbookChartIndex := 0;
end; end;
end. end.

View File

@ -390,24 +390,12 @@ type
constructor Create(AChart: TsChart); override; constructor Create(AChart: TsChart); override;
end; end;
TsBubbleSeries = class(TsChartSeries)
// to do: inherited from ScatterSeries, but without symbols
private
FBubbleRange: TsChartRange;
public
constructor Create(AChart: TsChart); override;
destructor Destroy; override;
procedure SetBubbleRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
procedure SetBubbleRange(ASheet1: String; ARow1, ACol1: Cardinal; ASheet2: String; ARow2, ACol2: Cardinal);
property BubbleRange: TsChartRange read FBubbleRange;
end;
TsChartSeriesSymbol = ( TsChartSeriesSymbol = (
cssRect, cssDiamond, cssTriangle, cssTriangleDown, cssTriangleLeft, cssRect, cssDiamond, cssTriangle, cssTriangleDown, cssTriangleLeft,
cssTriangleRight, cssCircle, cssStar, cssX, cssPlus, cssAsterisk cssTriangleRight, cssCircle, cssStar, cssX, cssPlus, cssAsterisk
); );
TsLineSeries = class(TsChartSeries) TsCustomLineSeries = class(TsChartSeries)
private private
FSymbol: TsChartSeriesSymbol; FSymbol: TsChartSeriesSymbol;
FSymbolHeight: Double; // in mm FSymbolHeight: Double; // in mm
@ -417,9 +405,7 @@ type
FBorder: TsChartLine; FBorder: TsChartLine;
function GetSymbolFill: TsChartFill; function GetSymbolFill: TsChartFill;
procedure SetSymbolFill(Value: TsChartFill); procedure SetSymbolFill(Value: TsChartFill);
public protected
constructor Create(AChart: TsChart); override;
destructor Destroy; override;
property Symbol: TsChartSeriesSymbol read FSymbol write FSymbol; property Symbol: TsChartSeriesSymbol read FSymbol write FSymbol;
property SymbolBorder: TsChartLine read FBorder write FBorder; property SymbolBorder: TsChartLine read FBorder write FBorder;
property SymbolFill: TsChartFill read GetSymbolFill write SetSymbolFill; property SymbolFill: TsChartFill read GetSymbolFill write SetSymbolFill;
@ -427,6 +413,20 @@ type
property SymbolWidth: double read FSymbolWidth write FSymbolWidth; property SymbolWidth: double read FSymbolWidth write FSymbolWidth;
property ShowLines: Boolean read FShowLines write FShowLines; property ShowLines: Boolean read FShowLines write FShowLines;
property ShowSymbols: Boolean read FShowSymbols write FShowSymbols; property ShowSymbols: Boolean read FShowSymbols write FShowSymbols;
public
constructor Create(AChart: TsChart); override;
destructor Destroy; override;
end;
TsLineSeries = class(TsCustomLineSeries)
public
property Symbol;
property SymbolBorder;
property SymbolFill;
property SymbolHeight;
property SymbolWidth;
property ShowLines;
property ShowSymbols;
end; end;
TsPieSeries = class(TsChartSeries) TsPieSeries = class(TsChartSeries)
@ -487,7 +487,7 @@ type
destructor Destroy; override; destructor Destroy; override;
end; end;
TsScatterSeries = class(TsLineSeries) TsCustomScatterSeries = class(TsCustomLineSeries)
private private
FRegression: TsChartRegression; FRegression: TsChartRegression;
public public
@ -496,6 +496,28 @@ type
property Regression: TsChartRegression read FRegression write FRegression; property Regression: TsChartRegression read FRegression write FRegression;
end; end;
TsScatterSeries = class(TsCustomScatterSeries)
public
property Symbol;
property SymbolBorder;
property SymbolFill;
property SymbolHeight;
property SymbolWidth;
property ShowLines;
property ShowSymbols;
end;
TsBubbleSeries = class(TsCustomScatterSeries)
private
FBubbleRange: TsChartRange;
public
constructor Create(AChart: TsChart); override;
destructor Destroy; override;
procedure SetBubbleRange(ARow1, ACol1, ARow2, ACol2: Cardinal);
procedure SetBubbleRange(ASheet1: String; ARow1, ACol1: Cardinal; ASheet2: String; ARow2, ACol2: Cardinal);
property BubbleRange: TsChartRange read FBubbleRange;
end;
TsChartSeriesList = class(TFPObjectList) TsChartSeriesList = class(TFPObjectList)
private private
function GetItem(AIndex: Integer): TsChartSeries; function GetItem(AIndex: Integer): TsChartSeries;
@ -1428,9 +1450,9 @@ begin
end; end;
{ TsLineSeries } { TsCustomLineSeries }
constructor TsLineSeries.Create(AChart: TsChart); constructor TsCustomLineSeries.Create(AChart: TsChart);
begin begin
inherited Create(AChart); inherited Create(AChart);
FChartType := ctLine; FChartType := ctLine;
@ -1445,18 +1467,18 @@ begin
FBorder.Color := scBlack; FBorder.Color := scBlack;
end; end;
destructor TsLineSeries.Destroy; destructor TsCustomLineSeries.Destroy;
begin begin
FBorder.Free; FBorder.Free;
inherited; inherited;
end; end;
function TsLineSeries.GetSymbolFill: TsChartFill; function TsCustomLineSeries.GetSymbolFill: TsChartFill;
begin begin
Result := FFill; Result := FFill;
end; end;
procedure TsLineSeries.SetSymbolFill(Value: TsChartFill); procedure TsCustomLineSeries.SetSymbolFill(Value: TsChartFill);
begin begin
FFill := Value; FFill := Value;
end; end;
@ -1574,16 +1596,16 @@ begin
end; end;
{ TsScatterSeries } { TsCustomScatterSeries }
constructor TsScatterSeries.Create(AChart: TsChart); constructor TsCustomScatterSeries.Create(AChart: TsChart);
begin begin
inherited Create(AChart); inherited Create(AChart);
FChartType := ctScatter; FChartType := ctScatter;
FRegression := TsChartRegression.Create; FRegression := TsChartRegression.Create;
end; end;
destructor TsScatterSeries.Destroy; destructor TsCustomScatterSeries.Destroy;
begin begin
FRegression.Free; FRegression.Free;
inherited; inherited;

View File

@ -3202,8 +3202,8 @@ begin
series := AChart.Series[ASeriesIndex]; series := AChart.Series[ASeriesIndex];
// These are the x values of a scatter or bubble plot. // These are the x values of a scatter plot.
if (series is TsScatterSeries) or (series is TsBubbleSeries) then if (series is TsScatterSeries) then
begin begin
domainRangeX := GetSheetCellRangeString_ODS( domainRangeX := GetSheetCellRangeString_ODS(
series.XRange.GetSheet1Name, series.XRange.GetSheet2Name, series.XRange.GetSheet1Name, series.XRange.GetSheet2Name,

View File

@ -63,6 +63,7 @@ type
protected protected
FCurItem: TChartDataItem; FCurItem: TChartDataItem;
function BuildRangeStr(AIndex: TsXYLRange; AListSeparator: char = #0): String; function BuildRangeStr(AIndex: TsXYLRange; AListSeparator: char = #0): String;
procedure ClearRanges;
function CountValues(AIndex: TsXYLRange): Integer; function CountValues(AIndex: TsXYLRange): Integer;
function GetCount: Integer; override; function GetCount: Integer; override;
function GetItem(AIndex: Integer): PChartDataItem; override; function GetItem(AIndex: Integer): PChartDataItem; override;
@ -141,7 +142,7 @@ type
procedure UpdateAreaSeries(AWorkbookSeries: TsAreaSeries; AChartSeries: TAreaSeries); procedure UpdateAreaSeries(AWorkbookSeries: TsAreaSeries; AChartSeries: TAreaSeries);
procedure UpdateBarSeries(AWorkbookSeries: TsBarSeries; AChartSeries: TBarSeries); procedure UpdateBarSeries(AWorkbookSeries: TsBarSeries; AChartSeries: TBarSeries);
procedure UpdateBubbleSeries(AWorkbookSeries: TsBubbleSeries; AChartSeries: TBubbleSeries); procedure UpdateBubbleSeries(AWorkbookSeries: TsBubbleSeries; AChartSeries: TBubbleSeries);
procedure UpdateLineSeries(AWorkbookSeries: TsLineSeries; AChartSeries: TLineSeries); procedure UpdateCustomLineSeries(AWorkbookSeries: TsCustomLineSeries; AChartSeries: TLineSeries);
procedure UpdatePieSeries(AWorkbookSeries: TsPieSeries; AChartSeries: TPieSeries); procedure UpdatePieSeries(AWorkbookSeries: TsPieSeries; AChartSeries: TPieSeries);
procedure UpdatePolarSeries(AWorkbookSeries: TsRadarSeries; AChartSeries: TPolarSeries); procedure UpdatePolarSeries(AWorkbookSeries: TsRadarSeries; AChartSeries: TPolarSeries);
procedure UpdateScatterSeries(AWorkbookSeries: TsScatterSeries; AChartSeries: TLineSeries); procedure UpdateScatterSeries(AWorkbookSeries: TsScatterSeries; AChartSeries: TLineSeries);
@ -173,6 +174,7 @@ uses
type type
TBasicPointSeriesOpener = class(TBasicPointSeries); TBasicPointSeriesOpener = class(TBasicPointSeries);
TsCustomLineSeriesOpener = class(TsCustomLineSeries);
function mmToPx(mm: Double; ppi: Integer): Integer; function mmToPx(mm: Double; ppi: Integer): Integer;
begin begin
@ -304,15 +306,7 @@ end;
constructor TsWorkbookChartSource.Create(AOwner: TComponent); constructor TsWorkbookChartSource.Create(AOwner: TComponent);
begin begin
inherited; inherited;
SetLength(FRanges[rngX], 1); ClearRanges;
SetLength(FRanges[rngY], 1);
SetLength(FRanges[rngLabel], 1);
SetLength(FRanges[rngColor], 1);
SetLength(FWorksheets[rngX], 1);
SetLength(FWorksheets[rngY], 1);
SetLength(FWorksheets[rngLabel], 1);
Setlength(FWorksheets[rngColor], 1);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -380,6 +374,25 @@ begin
end; end;
end; end;
procedure TsWorkbookChartSource.ClearRanges;
begin
SetLength(FRanges[rngX], 1); FRanges[rngX, 0 ] := nil;
SetLength(FRanges[rngY], 1); FRanges[rngY, 0] := nil;
SetLength(FRanges[rngLabel], 1); FRanges[rngLabel, 0] := nil;
SetLength(FRanges[rngColor], 1); FRanges[rngColor, 0] := nil;
SetLength(FWorksheets[rngX], 1); FWorksheets[rngX, 0] := nil;
SetLength(FWorksheets[rngY], 1); FWorksheets[rngY, 0] := nil;
SetLength(FWorksheets[rngLabel], 1); FWorksheets[rngLabel, 0] := nil;
SetLength(FWorksheets[rngColor], 1); FWorksheets[rngColor, 0] := nil;
FRangeStr[rngX] := '';
FRangeStr[rngY] := '';
FRangeStr[rngLabel] := '';
FRangeStr[rngColor] := '';
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Counts the number of x or y values contained in the x/y ranges Counts the number of x or y values contained in the x/y ranges
@ -620,7 +633,10 @@ begin
// Workbook has been successfully loaded, all sheets are ready // Workbook has been successfully loaded, all sheets are ready
if (lniWorkbook in AChangedItems) then if (lniWorkbook in AChangedItems) then
begin
ClearRanges;
Prepare; Prepare;
end;
// Used worksheet has been renamed? // Used worksheet has been renamed?
if (lniWorksheetRename in AChangedItems) then if (lniWorksheetRename in AChangedItems) then
@ -896,7 +912,6 @@ begin
FWorkbookSource := AValue; FWorkbookSource := AValue;
if FWorkbookSource <> nil then if FWorkbookSource <> nil then
FWorkbookSource.AddListener(self); FWorkbookSource.AddListener(self);
// FWorkbook := GetWorkbook;
ListenerNotification([lniWorkbook, lniWorksheet]); ListenerNotification([lniWorkbook, lniWorksheet]);
Prepare; Prepare;
end; end;
@ -1017,6 +1032,8 @@ begin
Result := TBubbleSeries.Create(FChart); Result := TBubbleSeries.Create(FChart);
src.SetYRange(1, TsBubbleSeries(ASeries).BubbleRange); src.SetYRange(1, TsBubbleSeries(ASeries).BubbleRange);
end; end;
ctPie:
Result := TPieSeries.Create(FChart);
else else
exit(nil); exit(nil);
end; end;
@ -1079,7 +1096,7 @@ begin
ctBubble: ctBubble:
UpdateBubbleSeries(TsBubbleSeries(ASeries), TBubbleSeries(ser)); UpdateBubbleSeries(TsBubbleSeries(ASeries), TBubbleSeries(ser));
ctLine: ctLine:
UpdateLineSeries(TsLineSeries(ASeries), TLineSeries(ser)); UpdateCustomLineSeries(TsLineSeries(ASeries), TLineSeries(ser));
ctScatter: ctScatter:
UpdateScatterSeries(TsScatterSeries(ASeries), TLineSeries(ser)); UpdateScatterSeries(TsScatterSeries(ASeries), TLineSeries(ser));
ctPie, ctRing: ctPie, ctRing:
@ -1347,7 +1364,11 @@ end;
procedure TsWorkbookChartLink.ListenerNotification(AChangedItems: TsNotificationItems; procedure TsWorkbookChartLink.ListenerNotification(AChangedItems: TsNotificationItems;
AData: Pointer = nil); AData: Pointer = nil);
begin begin
// to be completed Unused(AData);
// Workbook has been successfully loaded, all sheets are ready
if (lniWorkbook in AChangedItems) then
ClearChart;
end; end;
procedure TsWorkbookChartLink.Notification(AComponent: TComponent; Operation: TOperation); procedure TsWorkbookChartLink.Notification(AComponent: TComponent; Operation: TOperation);
@ -1561,49 +1582,6 @@ begin
FChart.Frame.Visible := AWorkbookChart.PlotArea.Border.Style <> clsNoLine; FChart.Frame.Visible := AWorkbookChart.PlotArea.Border.Style <> clsNoLine;
end; end;
{
procedure TsWorkbookChartLink.UpdateBarSeries(AWorkbookChart: TsChart);
var
i, n: Integer;
ser: TBarSeries;
barWidth, totalBarWidth: Integer;
begin
if AWorkbookChart.GetChartType <> ctBar then
exit;
// Count the bar series
n := 0;
for i := 0 to AWorkbookChart.Series.Count-1 do
begin
if AWorkbookChart.Series[i].ChartType = ctBar then
inc(n);
end;
// Iterate over bar series to put them side-by-side or to stack them
totalBarWidth := 90;
barWidth := round(totalBarWidth / n);
for i := 0 to FChart.SeriesCount-1 do
if FChart.Series[i] is TBarSeries then
begin
ser := TBarSeries(FChart.Series[i]);
case AWorkbookChart.Stackmode of
csmSideBySide:
begin
ser.BarWidthPercent := barWidth;
ser.BarWidthStyle := bwPercentMin;
ser.BarOffsetPercent := round((i - (n - 1)/2)*barWidth);
end;
csmStacked:
ser.Stacked := true;
csmStackedPercentage:
begin
ser.Stacked := true;
end;
end;
end;
end;
}
procedure TsWorkbookChartLink.UpdateChartBrush(AWorkbookChart: TsChart; procedure TsWorkbookChartLink.UpdateChartBrush(AWorkbookChart: TsChart;
AWorkbookFill: TsChartFill; ABrush: TBrush); AWorkbookFill: TsChartFill; ABrush: TBrush);
var var
@ -1763,7 +1741,7 @@ begin
end; end;
end; end;
procedure TsWorkbookChartLink.UpdateLineSeries(AWorkbookSeries: TsLineSeries; procedure TsWorkbookChartLink.UpdateCustomLineSeries(AWorkbookSeries: TsCustomLineSeries;
AChartSeries: TLineSeries); AChartSeries: TLineSeries);
const const
POINTER_STYLES: array[TsChartSeriesSymbol] of TSeriesPointerstyle = ( POINTER_STYLES: array[TsChartSeriesSymbol] of TSeriesPointerstyle = (
@ -1781,19 +1759,20 @@ const
); );
var var
ppi: Integer; ppi: Integer;
openedWorkbookSeries: TsCustomLineSeriesOpener absolute AWorkbookSeries;
begin begin
ppi := GetParentForm(FChart).PixelsPerInch; ppi := GetParentForm(FChart).PixelsPerInch;
UpdateChartPen(AWorkbookSeries.Chart, AWorkbookSeries.Line, AChartSeries.LinePen); UpdateChartPen(AWorkbookSeries.Chart, AWorkbookSeries.Line, AChartSeries.LinePen);
AChartSeries.ShowLines := AWorkbookSeries.Line.Style <> clsNoLine; AChartSeries.ShowLines := AWorkbookSeries.Line.Style <> clsNoLine;
AChartSeries.ShowPoints := AWorkbookSeries.ShowSymbols; AChartSeries.ShowPoints := openedWorkbookSeries.ShowSymbols;
if AChartSeries.ShowPoints then if AChartSeries.ShowPoints then
begin begin
UpdateChartBrush(AWorkbookSeries.Chart, AWorkbookSeries.Fill, AChartSeries.Pointer.Brush); UpdateChartBrush(AWorkbookSeries.Chart, AWorkbookSeries.Fill, AChartSeries.Pointer.Brush);
AChartSeries.Pointer.Pen.Color := AChartSeries.LinePen.Color; AChartSeries.Pointer.Pen.Color := AChartSeries.LinePen.Color;
AChartSeries.Pointer.Style := POINTER_STYLES[AWorkbookSeries.Symbol]; AChartSeries.Pointer.Style := POINTER_STYLES[openedWorkbookSeries.Symbol];
AChartSeries.Pointer.HorizSize := mmToPx(AWorkbookSeries.SymbolWidth, ppi); AChartSeries.Pointer.HorizSize := mmToPx(openedWorkbookSeries.SymbolWidth, ppi);
AChartSeries.Pointer.VertSize := mmToPx(AWorkbookSeries.SymbolHeight, ppi); AChartSeries.Pointer.VertSize := mmToPx(openedWorkbookSeries.SymbolHeight, ppi);
end; end;
AChartSeries.Stacked := AWorkbookSeries.Chart.StackMode <> csmSideBySide; AChartSeries.Stacked := AWorkbookSeries.Chart.StackMode <> csmSideBySide;
if AChartSeries.Source is TCalculatedChartSource then if AChartSeries.Source is TCalculatedChartSource then
@ -1838,7 +1817,7 @@ var
ser: TFitSeries; ser: TFitSeries;
s: String; s: String;
begin begin
UpdateLineSeries(AWorkbookSeries, AChartSeries); UpdateCustomLineSeries(AWorkbookSeries, AChartSeries);
if AWorkbookSeries.Regression.RegressionType = rtNone then if AWorkbookSeries.Regression.RegressionType = rtNone then
exit; exit;

View File

@ -1331,12 +1331,21 @@ var
begin begin
for j:=0 to FListeners.Count-1 do begin for j:=0 to FListeners.Count-1 do begin
C := TComponent(FListeners[j]); C := TComponent(FListeners[j]);
if C.GetInterface(GUID_SpreadsheetControl, I) then if (C <> nil) then
I.ListenerNotification(AChangedItems, AData) begin
else if C.GetInterface(GUID_SpreadsheetControl, I) then
raise Exception.CreateFmt('[TsWorkbookSource.NotifyListeners] Class %s is not prepared to be a spreadsheet listener.', I.ListenerNotification(AChangedItems, AData)
[C.ClassName]); else
raise Exception.CreateFmt('[TsWorkbookSource.NotifyListeners] Class %s is not prepared to be a spreadsheet listener.',
[C.ClassName]);
end;
end; end;
// Cleanup listener list from removed listeners (= set to nil) while
// NotifyListeners was running
for j := FListeners.Count-1 downto 0 do
if FListeners[j] = nil then
FListeners.Delete(j);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -1355,11 +1364,20 @@ begin
C := TComponent(FListeners[j]); C := TComponent(FListeners[j]);
if C = AListener then if C = AListener then
begin begin
FListeners.Delete(j); FListeners[j] := nil;
if C.GetInterface(GUID_SpreadsheetControl, I) then // Do not delete the listener here (FListeners.Delete(j)) because
I.RemoveWorkbookSource // RemoveListeners may be called while NotifyListeners is still running.
else // The problem can be that a chart may destroy a listening chart source
raise Exception.CreateFmt('Class %s not prepared for listening.',[AListener.ClassName]); // which would trigger RemoveListener. If the chart source then would be
// deleted from the list the NotifiyListeners loop would access
// unallocated memory --> crash
if C <> nil then
begin
if C.GetInterface(GUID_SpreadsheetControl, I) then
I.RemoveWorkbookSource
else
raise Exception.CreateFmt('Class %s not prepared for listening.',[AListener.ClassName]);
end;
end; end;
end; end;
end; end;