mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 21:09:28 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			181 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| 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.
 | |
| 
 | 
