mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-04 19:03:51 +02:00
638 lines
16 KiB
ObjectPascal
638 lines
16 KiB
ObjectPascal
unit ceChartEditor;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ButtonPanel, ComCtrls,
|
|
ExtCtrls, StdCtrls, Buttons,
|
|
TAGraph, TAChartAxis, TACustomSeries, TASeries, TAChartImageList,
|
|
ceAxisFrame;
|
|
|
|
type
|
|
|
|
{ TChartEditorForm }
|
|
|
|
TChartEditorForm = class(TForm)
|
|
CloseButton: TPanelBitBtn;
|
|
ButtonPanel: TButtonPanel;
|
|
Image1: TImage;
|
|
Label1: TLabel;
|
|
Notebook: TNotebook;
|
|
TitlePanel: TPanel;
|
|
Splitter1: TSplitter;
|
|
Tree: TTreeView;
|
|
procedure FormActivate(Sender: TObject);
|
|
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure OKButtonClick(Sender: TObject);
|
|
procedure TreeChanging(Sender: TObject; Node: TTreeNode;
|
|
var AllowChange: Boolean);
|
|
procedure TreeDeletion(Sender: TObject; Node: TTreeNode);
|
|
procedure TreeSelectionChanged(Sender: TObject);
|
|
private
|
|
FChart: TChart;
|
|
FSavedChartStream: TMemoryStream;
|
|
FSavedSeriesStreams: array of TMemoryStream;
|
|
FTitleNode: TTreeNode;
|
|
FFooterNode: TTreeNode;
|
|
FLegendNode: TTreeNode;
|
|
FAxesNode: TTreeNode;
|
|
FSeriesNode: TTreeNode;
|
|
FOKClicked: Boolean;
|
|
FApplyButton: TBitBtn;
|
|
function AddFrame(AParentNode: TTreeNode; ACaption: String; AFrame: TFrame;
|
|
AImageIndex: Integer): TTreeNode;
|
|
procedure ApplyButtonClick(Sender: TObject);
|
|
procedure FindComponentClass({%H-}AReader: TReader; const AClassName: String;
|
|
var AClass: TComponentClass);
|
|
function GetPageIndexOfNode(ANode: TTreeNode): Integer;
|
|
procedure SeriesChangedHandler(Sender: TObject);
|
|
procedure SetChart(AValue: TChart);
|
|
procedure UpdateImages;
|
|
protected
|
|
procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer;
|
|
{%H-}WithThemeSpace: Boolean); override;
|
|
procedure PopulateAxes(AChart: TChart);
|
|
procedure PopulateSeries(AChart: TChart);
|
|
procedure SaveChartToStream;
|
|
procedure SelectNode(ANode: TTreeNode);
|
|
procedure RestoreChartFromStream;
|
|
function Validate(ANode: TTreeNode; out AMsg: String; out AControl: TWinControl): boolean;
|
|
public
|
|
procedure SelectAxis(AxisIndex: Integer; APage: TChartAxisEditorPage);
|
|
procedure SelectFooter;
|
|
procedure SelectLegend;
|
|
procedure SelectSeries(ASeriesIndex: Integer);
|
|
procedure SelectTitle;
|
|
property Chart: TChart read FChart write SetChart;
|
|
end;
|
|
|
|
var
|
|
ChartEditorForm: TChartEditorForm;
|
|
|
|
procedure EditChartTitle(AChart: TChart);
|
|
procedure EditChartFooter(AChart: TChart);
|
|
procedure EditChartLegend(AChart: TChart);
|
|
procedure EditChartAxis(AChartAxis: TChartAxis; APage: TChartAxisEditorPage);
|
|
procedure EditChartSeries(ASeries: TBasicChartSeries);
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
uses
|
|
LResources, Math,
|
|
TAEnumerators,
|
|
ceTitleFootFrame, ceLegendFrame, ceSeriesFrame, ceImages;
|
|
|
|
const
|
|
TITLE_NODE_NAME = 'Title';
|
|
FOOTER_NODE_NAME = 'Footer';
|
|
LEGEND_NODE_NAME = 'Legend';
|
|
AXIS_NODE_NAME = 'Axes';
|
|
SERIES_NODE_NAME = 'Series';
|
|
|
|
{ Helper procedures }
|
|
|
|
function CreateChartEditorForm(AChart: TChart): TChartEditorForm;
|
|
begin
|
|
Result := TChartEditorForm.Create(nil);
|
|
Result.Position := poScreenCenter;
|
|
Result.Chart := AChart;
|
|
end;
|
|
|
|
procedure SelectChartElement(AChart: TChart; ATreeCaption: String);
|
|
var
|
|
F: TChartEditorForm;
|
|
begin
|
|
F := TChartEditorForm.Create(nil);
|
|
try
|
|
F.Position := poScreenCenter;
|
|
F.Chart := AChart;
|
|
F.Tree.Selected := F.Tree.Items.FindNodeWithText(ATreeCaption);
|
|
F.TreeSelectionChanged(nil);
|
|
F.ShowModal;
|
|
finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
{ Global procedures }
|
|
|
|
procedure EditChartTitle(AChart: TChart);
|
|
var
|
|
F: TChartEditorForm;
|
|
begin
|
|
F := CreateChartEditorForm(AChart);
|
|
try
|
|
F.SelectTitle;
|
|
F.ShowModal;
|
|
finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure EditChartFooter(AChart: TChart);
|
|
var
|
|
F: TChartEditorForm;
|
|
begin
|
|
F := CreateChartEditorForm(AChart);
|
|
try
|
|
F.SelectFooter;
|
|
F.ShowModal;
|
|
finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure EditChartLegend(AChart: TChart);
|
|
var
|
|
F: TChartEditorForm;
|
|
begin
|
|
F := CreateChartEditorForm(AChart);
|
|
try
|
|
F.SelectLegend;
|
|
F.ShowModal;
|
|
finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure EditChartAxis(AChartAxis: TChartAxis; APage: TChartAxisEditorPage);
|
|
var
|
|
F: TChartEditorForm;
|
|
begin
|
|
F := CreateChartEditorForm(AChartAxis.GetChart as TChart);
|
|
try
|
|
F.SelectAxis(AChartAxis.Index, APage);
|
|
F.ShowModal;
|
|
finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure EditChartSeries(ASeries: TBasicChartSeries);
|
|
var
|
|
F: TChartEditorForm;
|
|
begin
|
|
if not ( (ASeries is TLineSeries) or
|
|
(ASeries is TBarSeries) or
|
|
(ASeries is TAreaSeries)
|
|
) then
|
|
begin
|
|
raise Exception.Create('Series type not supported for direct editing.');
|
|
end;
|
|
|
|
F := CreateChartEditorForm(ASeries.ParentChart);
|
|
try
|
|
F.SelectSeries(ASeries.Index);
|
|
F.ShowModal;
|
|
finally
|
|
F.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TChartEditorForm }
|
|
|
|
function TChartEditorForm.AddFrame(AParentNode: TTreeNode; ACaption: String;
|
|
AFrame: TFrame; AImageIndex: Integer): TTreeNode;
|
|
var
|
|
page: TPage;
|
|
begin
|
|
NoteBook.Pages.Add(ACaption);
|
|
page := NoteBook.Page[Notebook.PageCount-1];
|
|
AFrame.Parent := page;
|
|
AFrame.Name := '';
|
|
AFrame.Align := alClient;
|
|
Result := Tree.Items.AddChildObject(AParentNode, ACaption, AFrame);
|
|
Result.ImageIndex := AImageIndex;
|
|
Result.SelectedIndex := AImageIndex;
|
|
end;
|
|
|
|
procedure TChartEditorForm.ApplyButtonClick(Sender: TObject);
|
|
var
|
|
msg: String;
|
|
C: TWinControl;
|
|
begin
|
|
if not Validate(Tree.Selected, msg, C) then
|
|
begin
|
|
C.SetFocus;
|
|
MessageDlg(msg, mtError, [mbOK], 0);
|
|
ModalResult := mrNone;
|
|
end else
|
|
SaveChartToStream;
|
|
end;
|
|
(*
|
|
procedure TChartEditorForm.CloseButtonClick(Sender: TObject);
|
|
var
|
|
msg: String;
|
|
C: TWinControl;
|
|
begin
|
|
if not Validate(Tree.Selected, msg, C) then
|
|
begin
|
|
C.SetFocus;
|
|
MessageDlg(msg, mtError, [mbOK], 0);
|
|
ModalResult := mrNone;
|
|
end else
|
|
RestoreChartFromStream;
|
|
end;
|
|
*)
|
|
procedure TChartEditorForm.FormActivate(Sender: TObject);
|
|
var
|
|
w: Integer = 0;
|
|
h: Integer = 0;
|
|
begin
|
|
GetPreferredSize(w, h);
|
|
Constraints.MinWidth := w;
|
|
Constraints.MinHeight := h;
|
|
end;
|
|
|
|
procedure TChartEditorForm.CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer;
|
|
{%H-}WithThemeSpace: Boolean);
|
|
var
|
|
w: Integer = 0;
|
|
h: Integer = 0;
|
|
wm, hm: Integer;
|
|
node: TTreeNode;
|
|
begin
|
|
TChartTitleFootFrame(FTitleNode.Data).GetPreferredSize(w, h);
|
|
wm := w;
|
|
hm := h;
|
|
|
|
TChartLegendFrame(FLegendNode.Data).GetPreferredSize(w, h);
|
|
wm := Max(w, wm);
|
|
hm := Max(h, hm);
|
|
|
|
node := FAxesNode.GetFirstChild;
|
|
if node <> nil then
|
|
begin
|
|
TChartAxisFrame(node.Data).GetPreferredSize(w, h);
|
|
wm := Max(w, wm);
|
|
hm := Max(h, hm);
|
|
end;
|
|
|
|
node := FSeriesNode.GetFirstChild;
|
|
if node <> nil then
|
|
begin
|
|
TChartSeriesFrame(node.Data).GetPreferredSize(w, h);
|
|
wm := Max(w, wm);
|
|
hm := Max(h, hm);
|
|
end;
|
|
|
|
PreferredWidth := Tree.Constraints.MinWidth +
|
|
Tree.BorderSpacing.Left + Tree.Borderspacing.Right + Splitter1.Width +
|
|
wm + Notebook.BorderSpacing.Left + Notebook.BorderSpacing.Right;
|
|
|
|
PreferredHeight := TitlePanel.Height + TitlePanel.BorderSpacing.Top + hm +
|
|
Notebook.BorderSpacing.Top + Notebook.BorderSpacing.Bottom +
|
|
ButtonPanel.Height + 2*ButtonPanel.BorderSpacing.Around;
|
|
end;
|
|
|
|
// Adapted from private TChart method
|
|
procedure TChartEditorForm.FindComponentClass(AReader: TReader;
|
|
const AClassName: String; var AClass: TComponentClass);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if AClassName = FChart.ClassName then begin
|
|
AClass := TChart;
|
|
exit;
|
|
end;
|
|
for i := 0 to SeriesClassRegistry.Count - 1 do begin
|
|
AClass := TSeriesClass(SeriesClassRegistry.GetClass(i));
|
|
if AClass.ClassNameIs(AClassName) then exit;
|
|
end;
|
|
AClass := nil;
|
|
end;
|
|
|
|
procedure TChartEditorForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
|
begin
|
|
if not CanClose then
|
|
exit;
|
|
if not FOKClicked then
|
|
RestoreChartFromStream;
|
|
end;
|
|
|
|
procedure TChartEditorForm.FormCreate(Sender: TObject);
|
|
begin
|
|
ceImages.ChartImagesDM.ChartImages.GetBitmap(7, ButtonPanel.CloseButton.Glyph);
|
|
Tree.Items.BeginUpdate;
|
|
try
|
|
Tree.Items.Clear;
|
|
FTitleNode := AddFrame(nil, TITLE_NODE_NAME, TChartTitleFootFrame.Create(self), 0);
|
|
FFooterNode := AddFrame(nil, FOOTER_NODE_NAME, TChartTitleFootFrame.Create(self), 1);
|
|
FLegendNode := AddFrame(nil, LEGEND_NODE_NAME, TChartLegendFrame.Create(self), 2);
|
|
FAxesNode := Tree.Items.AddChildObject(nil, AXIS_NODE_NAME, nil);
|
|
FSeriesNode := Tree.Items.AddChildObject(nil, SERIES_NODE_NAME, nil);
|
|
Tree.FullExpand;
|
|
finally
|
|
Tree.Items.EndUpdate;
|
|
end;
|
|
|
|
FApplyButton := TBitBtn.Create(ButtonPanel);
|
|
FApplyButton.Caption := 'Apply';
|
|
FApplyButton.Images := ChartImagesDM.ChartImages;
|
|
FApplyButton.ImageIndex := 7;
|
|
FApplyButton.AutoSize := true;
|
|
FApplyButton.OnClick := @ApplyButtonClick;
|
|
FApplyButton.AnchorSideTop.Control := ButtonPanel.OKButton;
|
|
FApplyButton.Parent := ButtonPanel;
|
|
|
|
AutoSize := true;
|
|
end;
|
|
|
|
procedure TChartEditorForm.FormDestroy(Sender: TObject);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
FSavedChartStream.Free;
|
|
for i:=0 to FChart.SeriesCount-1 do
|
|
FSavedSeriesStreams[i].Free;
|
|
end;
|
|
|
|
function TChartEditorForm.GetPageIndexOfNode(ANode: TTreeNode): Integer;
|
|
var
|
|
i: Integer;
|
|
page: TPage;
|
|
frame: TFrame;
|
|
begin
|
|
frame := TFrame(ANode.Data);
|
|
for i := 0 to Notebook.PageCount-1 do
|
|
begin
|
|
page := Notebook.Page[i];
|
|
if page.ContainsControl(frame) then
|
|
begin
|
|
Result := i;
|
|
exit;
|
|
end;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
procedure TChartEditorForm.OKButtonClick(Sender: TObject);
|
|
var
|
|
msg: String;
|
|
C: TWinControl;
|
|
begin
|
|
if not Validate(Tree.selected, msg, C) then
|
|
begin
|
|
C.SetFocus;
|
|
MessageDlg(msg, mtError, [mbOK], 0);
|
|
ModalResult := mrNone;
|
|
end else
|
|
FOKClicked := true;
|
|
end;
|
|
|
|
procedure TChartEditorForm.TreeChanging(Sender: TObject; Node: TTreeNode;
|
|
var AllowChange: Boolean);
|
|
var
|
|
msg: String;
|
|
C: TWinControl;
|
|
begin
|
|
if not Validate(Node, msg, C) then
|
|
begin
|
|
C.SetFocus;
|
|
MessageDlg(msg, mtError, [mbOk], 0);
|
|
AllowChange := false;
|
|
end;
|
|
end;
|
|
|
|
procedure TChartEditorForm.PopulateAxes(AChart: TChart);
|
|
var
|
|
i: Integer;
|
|
frame: TChartAxisFrame;
|
|
axis: TChartAxis;
|
|
imgIdx: Integer;
|
|
begin
|
|
FAxesNode.DeleteChildren;
|
|
if AChart <> nil then
|
|
begin
|
|
for i := 0 to AChart.AxisList.Count-1 do
|
|
begin
|
|
axis := AChart.AxisList.Axes[i];
|
|
frame := TChartAxisFrame.Create(self);
|
|
frame.Prepare(axis);
|
|
imgIdx := ord(axis.Alignment) + 3;
|
|
AddFrame(FAxesNode, axis.DisplayName, frame, imgIdx);
|
|
end;
|
|
FAxesNode.Expand(true);
|
|
end;
|
|
end;
|
|
|
|
procedure TChartEditorForm.PopulateSeries(AChart: TChart);
|
|
var
|
|
ser: TCustomChartSeries;
|
|
frame: TChartSeriesFrame;
|
|
imgIdx: Integer;
|
|
begin
|
|
FSeriesNode.DeleteChildren;
|
|
if AChart <> nil then
|
|
begin
|
|
ChartImagesDM.ChartImages.Chart := nil;
|
|
imgIdx := ChartImagesDM.ChartImages.Count;
|
|
ChartImagesDM.ChartImages.Chart := AChart;
|
|
for ser in CustomSeries(AChart) do
|
|
begin
|
|
frame := TChartSeriesFrame.Create(self);
|
|
frame.Prepare(ser);
|
|
frame.OnChanged := @SeriesChangedHandler;
|
|
AddFrame(FSeriesNode, ser.Title, frame, imgIdx);
|
|
inc(imgIdx);
|
|
end;
|
|
FSeriesNode.Expand(true);
|
|
end;
|
|
end;
|
|
|
|
procedure TChartEditorForm.RestoreChartFromStream;
|
|
var
|
|
i: Integer;
|
|
ser: TBasicChartSeries;
|
|
begin
|
|
FSavedChartStream.Position := 0;
|
|
ReadComponentFromTextStream(FSavedChartStream, TComponent(FChart), @FindComponentClass);
|
|
for i := 0 to FChart.SeriesCount-1 do
|
|
begin
|
|
FSavedSeriesStreams[i].Position := 0;
|
|
ser := nil;
|
|
ReadComponentFromTextStream(FSavedSeriesStreams[i], TComponent(ser), @FindComponentClass);
|
|
FChart.Series[i].Assign(ser);
|
|
end;
|
|
FChart.Invalidate;
|
|
end;
|
|
|
|
procedure TChartEditorForm.SaveChartToStream;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
FSavedChartStream.Position := 0;
|
|
WriteComponentAsTextToStream(FSavedChartStream, FChart);
|
|
for i := 0 to FChart.SeriesCount-1 do
|
|
WriteComponentAsTextToStream(FSavedSeriesStreams[i], FChart.Series[i]);
|
|
end;
|
|
|
|
procedure TChartEditorForm.SelectAxis(AxisIndex: Integer; APage: TChartAxisEditorPage);
|
|
var
|
|
node: TTreeNode;
|
|
idx: Integer;
|
|
frame: TChartAxisFrame;
|
|
begin
|
|
idx := 0;
|
|
node := FAxesNode.GetFirstChild;
|
|
while node <> nil do begin
|
|
if idx = AxisIndex then
|
|
begin
|
|
SelectNode(node);
|
|
frame := TChartAxisFrame(node.Data);
|
|
frame.Page := APage;
|
|
exit;
|
|
end;
|
|
node := node.GetNextSibling;
|
|
inc(idx);
|
|
end;
|
|
end;
|
|
|
|
procedure TChartEditorForm.SelectFooter;
|
|
begin
|
|
SelectNode(FFooterNode);
|
|
end;
|
|
|
|
procedure TChartEditorForm.SelectLegend;
|
|
begin
|
|
SelectNode(FLegendNode);
|
|
end;
|
|
|
|
procedure TChartEditorForm.SelectNode(ANode: TTreeNode);
|
|
begin
|
|
Tree.Selected := ANode;
|
|
TreeSelectionChanged(nil);
|
|
end;
|
|
|
|
procedure TChartEditorForm.SelectSeries(ASeriesIndex: Integer);
|
|
var
|
|
idx: Integer;
|
|
node: TTreeNode;
|
|
begin
|
|
idx := 0;
|
|
node := FSeriesNode.GetFirstChild;
|
|
while (node <> nil) do
|
|
begin
|
|
if idx = ASeriesIndex then
|
|
begin
|
|
SelectNode(node);
|
|
exit;
|
|
end;
|
|
node := node.GetNextSibling;
|
|
inc(idx);
|
|
end;
|
|
end;
|
|
|
|
procedure TChartEditorForm.SelectTitle;
|
|
begin
|
|
SelectNode(FTitleNode);
|
|
end;
|
|
|
|
procedure TChartEditorForm.SeriesChangedHandler(Sender: TObject);
|
|
begin
|
|
UpdateImages;
|
|
end;
|
|
|
|
procedure TChartEditorForm.SetChart(AValue: TChart);
|
|
var
|
|
titleFrame: TChartTitleFootFrame;
|
|
legendFrame: TChartLegendFrame;
|
|
i: Integer;
|
|
begin
|
|
if FChart = AValue then
|
|
exit;
|
|
|
|
FChart := AValue;
|
|
|
|
// Save inital chart and series properties for restoring when Cancel is pressed.
|
|
if FSavedChartStream = nil then
|
|
begin
|
|
FSavedChartStream := TMemoryStream.Create;
|
|
SetLength(FSavedSeriesStreams, FChart.SeriesCount);
|
|
for i := 0 to FChart.SeriesCount-1 do
|
|
FSavedSeriesStreams[i] := TMemoryStream.Create;
|
|
end else
|
|
begin
|
|
FSavedChartStream.Clear;
|
|
for i := 0 to FChart.SeriesCount-1 do
|
|
FSavedSeriesStreams[i].Clear;
|
|
end;
|
|
SaveChartToStream;
|
|
|
|
titleFrame := TChartTitleFootFrame(FTitleNode.Data);
|
|
titleFrame.Prepare(FChart.Title);
|
|
|
|
titleFrame := TChartTitleFootFrame(FFooterNode.Data);
|
|
titleFrame.Prepare(FChart.Foot);
|
|
|
|
legendFrame := TChartLegendFrame(FLegendNode.Data);
|
|
legendFrame.Prepare(FChart.Legend);
|
|
|
|
PopulateAxes(FChart);
|
|
PopulateSeries(FChart);
|
|
|
|
FOKClicked := false;
|
|
end;
|
|
|
|
procedure TChartEditorForm.TreeDeletion(Sender: TObject; Node: TTreeNode);
|
|
var
|
|
pageIdx: Integer;
|
|
begin
|
|
if (Node.Data = nil) or (csDestroying in ComponentState) then
|
|
exit;
|
|
pageIdx := GetPageIndexOfNode(Node);
|
|
if pageIdx > -1 then
|
|
Notebook.Page[pageIdx].Free; // Page owns and destroys the frame
|
|
end;
|
|
|
|
procedure TChartEditorForm.TreeSelectionChanged(Sender: TObject);
|
|
var
|
|
pageIdx: Integer;
|
|
s: String;
|
|
begin
|
|
pageIdx := GetPageIndexOfNode(Tree.Selected);
|
|
if pageIdx > -1 then
|
|
begin
|
|
Notebook.PageIndex := pageIdx;
|
|
s := Tree.Selected.Text;
|
|
if Tree.Selected.Parent = FAxesNode then
|
|
Label1.Caption := 'Axis: ' + s
|
|
else if Tree.Selected.Parent = FSeriesNode then
|
|
Label1.Caption := 'Series: "' + s + '"'
|
|
else
|
|
Label1.Caption := s;
|
|
ChartImagesDM.ChartImages.GetBitmap(Tree.Selected.ImageIndex, Image1.Picture.Bitmap);
|
|
end;
|
|
end;
|
|
|
|
procedure TChartEditorForm.UpdateImages;
|
|
begin
|
|
ChartImagesDM.ChartImages.Chart := nil;
|
|
ChartImagesDM.ChartImages.Chart := FChart;
|
|
ChartImagesDM.ChartImages.GetBitmap(Tree.Selected.ImageIndex, Image1.Picture.Bitmap);
|
|
end;
|
|
|
|
function TChartEditorForm.Validate(ANode: TTreeNode; out AMsg: String;
|
|
out AControl: TWinControl): Boolean;
|
|
begin
|
|
if ANode = nil then
|
|
exit(true);
|
|
|
|
if TObject(ANode.Data) is TChartAxisFrame then
|
|
begin
|
|
Result := TChartAxisFrame(ANode.Data).Validate(AMsg, AControl);
|
|
if not Result then exit;
|
|
end;
|
|
Result := true;
|
|
end;
|
|
|
|
end.
|
|
|