File: tachartextentlink.pas

package info (click to toggle)
lazarus 2.0.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 214,460 kB
  • sloc: pascal: 1,862,622; xml: 265,709; cpp: 56,595; sh: 3,008; java: 609; makefile: 535; perl: 297; sql: 222; ansic: 137
file content (180 lines) | stat: -rw-r--r-- 4,313 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
unit TAChartExtentLink;

{$H+}

interface

uses
  Classes, TAChartUtils, TAGraph;

type
  TLinkedChart = class(TCollectionItem)
  strict private
    FChart: TChart;
    FListener: TListener;
    procedure OnExtentChanged(ASender: TObject);
    procedure SetChart(AValue: TChart);
  protected
    function GetDisplayName: String; override;
  public
    constructor Create(ACollection: TCollection); override;
    destructor Destroy; override;
  published
    property Chart: TChart read FChart write SetChart;
  end;

  TLinkedCharts = class(TCollection)
  strict private
    FOwner: TComponent;
  protected
    function GetOwner: TPersistent; override;
  public
    constructor Create(AOwner: TComponent);
    function Add: TLinkedChart; // Should be inline, but FPC 2.6 miscompiles it.
  end;

  TChartExtendLinkMode = (elmXY, elmOnlyX, elmOnlyY);

  TChartExtentLink = class(TComponent)
  strict private
    FEnabled: Boolean;
    FLinkedCharts: TLinkedCharts;
    FMode: TChartExtendLinkMode;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure AddChart(AChart: TChart);
    procedure SyncWith(AChart: TChart);
  published
    property Enabled: Boolean read FEnabled write FEnabled default true;
    property LinkedCharts: TLinkedCharts read FLinkedCharts write FLinkedCharts;
    property Mode: TChartExtendLinkMode read FMode write FMode default elmXY;
  end;

procedure Register;

implementation

uses
  SysUtils, TAGeometry;

procedure Register;
begin
  RegisterComponents(CHART_COMPONENT_IDE_PAGE, [TChartExtentLink]);
end;

{ TLinkedCharts }

function TLinkedCharts.Add: TLinkedChart;
begin
  Result := TLinkedChart(inherited Add);
end;

constructor TLinkedCharts.Create(AOwner: TComponent);
begin
  inherited Create(TLinkedChart);
  FOwner := AOwner;
end;

function TLinkedCharts.GetOwner: TPersistent;
begin
  Result := FOwner;
end;

{ TLinkedChart }

constructor TLinkedChart.Create(ACollection: TCollection);
begin
  inherited Create(ACollection);
  FListener := TListener.Create(@FChart, @OnExtentChanged);
end;

destructor TLinkedChart.Destroy;
begin
  FreeAndNil(FListener);
  inherited;
end;

function TLinkedChart.GetDisplayName: String;
begin
  Result := inherited GetDisplayName;
  if Chart <> nil then
    Result += ' -> ' + Chart.Name;
end;

procedure TLinkedChart.OnExtentChanged(ASender: TObject);
begin
  Unused(ASender);
  (Collection.Owner as TChartExtentLink).SyncWith(Chart);
end;

procedure TLinkedChart.SetChart(AValue: TChart);
begin
  if FChart = AValue then exit;
  if Chart <> nil then
    Chart.ExtentBroadcaster.Unsubscribe(FListener);
  FChart := AValue;
  if Chart <> nil then
    Chart.ExtentBroadcaster.Subscribe(FListener);
end;

{ TChartExtentLink }

procedure TChartExtentLink.AddChart(AChart: TChart);
var
  i: TCollectionItem;
begin
  for i in LinkedCharts do
    if TLinkedChart(i).Chart = AChart then exit;
  LinkedCharts.Add.Chart := AChart;
end;

constructor TChartExtentLink.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEnabled := true;
  FLinkedCharts := TLinkedCharts.Create(Self);
end;

destructor TChartExtentLink.Destroy;
begin
  FreeAndNil(FLinkedCharts);
  inherited;
end;

procedure TChartExtentLink.SyncWith(AChart: TChart);

  function CombineXY(const AX, AY: TDoubleRect): TDoubleRect;
  begin
    Result.a := DoublePoint(AX.a.X, AY.a.Y);
    Result.b := DoublePoint(AX.b.X, AY.b.Y);
  end;

var
  c: TCollectionItem;
  ch: TChart;
begin
  if not FEnabled or (AChart = nil) then exit;
  for c in LinkedCharts do begin
    ch := TLinkedChart(c).Chart;
    // Do not sync if the chart was never drawn yet.
    if (ch = nil) or (ch.LogicalExtent = EmptyExtent) then continue;
    // ZoomFull is lazy by default, so full extent may be not recalculated yet.
    if not ch.IsZoomed and (ch <> AChart) then
      ch.LogicalExtent := ch.GetFullExtent;
    // An event loop will be broken since setting LogicalExtent to
    // the same value does not initiale the extent broadcast.
    case Mode of
      elmXY:
        ch.LogicalExtent := AChart.LogicalExtent;
      elmOnlyX:
        ch.LogicalExtent := CombineXY(AChart.LogicalExtent, ch.LogicalExtent);
      elmOnlyY:
        ch.LogicalExtent := CombineXY(ch.LogicalExtent, AChart.LogicalExtent);
    end;
  end;
end;

end.