mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-12-05 17:57:09 +01:00
TAChart: New TSortedChartSource. Issue #35356, patch by Marcin Wiazowski.
git-svn-id: trunk@61251 -
This commit is contained in:
parent
475be67649
commit
adc9225cec
@ -99,6 +99,7 @@ type
|
||||
procedure SetY(const AValue: Double);
|
||||
procedure MultiplyY(const ACoeff: Double);
|
||||
function Point: TDoublePoint; inline;
|
||||
procedure MakeUnique;
|
||||
end;
|
||||
PChartDataItem = ^TChartDataItem;
|
||||
|
||||
@ -237,7 +238,7 @@ type
|
||||
procedure EndUpdate; override;
|
||||
public
|
||||
class procedure CheckFormat(const AFormat: String);
|
||||
function BasicExtent: TDoubleRect;
|
||||
function BasicExtent: TDoubleRect; virtual;
|
||||
function Extent: TDoubleRect; virtual;
|
||||
function ExtentCumulative: TDoubleRect; virtual;
|
||||
function ExtentList: TDoubleRect; virtual;
|
||||
@ -538,6 +539,15 @@ begin
|
||||
Result := YList[AIndex - 1];
|
||||
end;
|
||||
|
||||
procedure TChartDataItem.MakeUnique;
|
||||
begin
|
||||
// using SetLength() is a documented way of making the dynamic array unique:
|
||||
// "the reference count after a call to SetLength will be 1"
|
||||
UniqueString(Text);
|
||||
SetLength(XList, Length(XList));
|
||||
SetLength(YList, Length(YList));
|
||||
end;
|
||||
|
||||
procedure TChartDataItem.MultiplyY(const ACoeff: Double);
|
||||
var
|
||||
i: Integer;
|
||||
|
||||
@ -75,6 +75,41 @@ type
|
||||
property OnCompare;
|
||||
end;
|
||||
|
||||
{ TSortedChartSource }
|
||||
|
||||
TSortedChartSource = class(TCustomSortedChartSource)
|
||||
strict private
|
||||
FListener: TListener;
|
||||
FListenerSelf: TListener;
|
||||
FOrigin: TCustomChartSource;
|
||||
procedure Changed(ASender: TObject);
|
||||
procedure SetOrigin(AValue: TCustomChartSource);
|
||||
protected
|
||||
function DoCompare(AItem1, AItem2: Pointer): Integer; override;
|
||||
function GetCount: Integer; override;
|
||||
function GetItem(AIndex: Integer): PChartDataItem; override;
|
||||
procedure ResetTransformation(ACount: Integer);
|
||||
procedure SetXCount(AValue: Cardinal); override;
|
||||
procedure SetYCount(AValue: Cardinal); override;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
destructor Destroy; override;
|
||||
published
|
||||
function BasicExtent: TDoubleRect; override;
|
||||
function Extent: TDoubleRect; override;
|
||||
function ExtentCumulative: TDoubleRect; override;
|
||||
function ExtentList: TDoubleRect; override;
|
||||
function ExtentXYList: TDoubleRect; override;
|
||||
function ValuesTotal: Double; override;
|
||||
property Origin: TCustomChartSource read FOrigin write SetOrigin;
|
||||
// Sorting
|
||||
property SortBy;
|
||||
property SortDir;
|
||||
property Sorted;
|
||||
property SortIndex;
|
||||
property OnCompare;
|
||||
end;
|
||||
|
||||
{ TMWCRandomGenerator }
|
||||
|
||||
// Mutliply-with-carry random number generator.
|
||||
@ -276,12 +311,11 @@ procedure Register;
|
||||
begin
|
||||
RegisterComponents(
|
||||
CHART_COMPONENT_IDE_PAGE, [
|
||||
TListChartSource, TRandomChartSource, TUserDefinedChartSource,
|
||||
TCalculatedChartSource
|
||||
TListChartSource, TSortedChartSource, TRandomChartSource,
|
||||
TUserDefinedChartSource, TCalculatedChartSource
|
||||
]);
|
||||
end;
|
||||
|
||||
|
||||
{ TListChartSourceStrings }
|
||||
|
||||
procedure TListChartSourceStrings.Clear;
|
||||
@ -481,7 +515,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
{ TListChartSource }
|
||||
|
||||
function TListChartSource.Add(
|
||||
@ -841,6 +874,196 @@ begin
|
||||
(FDataPoints as TListChartSourceStrings).LoadingFinished;
|
||||
end;
|
||||
|
||||
{ TSortedChartSource }
|
||||
|
||||
constructor TSortedChartSource.Create(AOwner: TComponent);
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
FXCount := MaxInt; // Allow source to be used by any series while Origin = nil
|
||||
FYCount := MaxInt;
|
||||
FListener := TListener.Create(@FOrigin, @Changed);
|
||||
FListenerSelf := TListener.Create(nil, @Changed);
|
||||
Broadcaster.Subscribe(FListenerSelf);
|
||||
end;
|
||||
|
||||
destructor TSortedChartSource.Destroy;
|
||||
begin
|
||||
ResetTransformation(0);
|
||||
FreeAndNil(FListenerSelf);
|
||||
FreeAndNil(FListener);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TSortedChartSource.Changed(ASender: TObject);
|
||||
begin
|
||||
if ASender = Self then begin
|
||||
// We can get here only due to FListenerSelf's notification.
|
||||
// If some of our own (not Origin's) sorting properties was changed and we
|
||||
// are sorted, then our Sort() method has been called, so the transformation
|
||||
// is valid; but if we are no longer sorted, only notification is sent (so
|
||||
// we are here), so we must reinitialize the transformation to return to
|
||||
// the transparent (i.e. unsorted) state.
|
||||
if not IsSorted then
|
||||
ResetTransformation(Count);
|
||||
exit;
|
||||
end;
|
||||
|
||||
if FOrigin <> nil then begin
|
||||
FXCount := Origin.XCount;
|
||||
FYCount := Origin.YCount;
|
||||
ResetTransformation(Origin.Count);
|
||||
if IsSorted and (not HasSameSorting(Origin)) then Sort else Notify;
|
||||
end else begin
|
||||
FXCount := MaxInt; // Allow source to be used by any series while Origin = nil
|
||||
FYCount := MaxInt;
|
||||
ResetTransformation(0);
|
||||
Notify;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSortedChartSource.DoCompare(AItem1, AItem2: Pointer): Integer;
|
||||
var
|
||||
item1, item2: TChartDataItem;
|
||||
begin
|
||||
// some data sources use same memory buffer for every item read,
|
||||
// so local copies must be made before comparing two items
|
||||
item1 := Origin.Item[PInteger(AItem1)^]^;
|
||||
|
||||
// avoid sharing same memory by item1's and item2's reference-
|
||||
// counted variables
|
||||
item1.MakeUnique;
|
||||
|
||||
item2 := Origin.Item[PInteger(AItem2)^]^;
|
||||
|
||||
Result := inherited DoCompare(@item1, @item2);
|
||||
end;
|
||||
|
||||
function TSortedChartSource.GetCount: Integer;
|
||||
begin
|
||||
if Origin <> nil then
|
||||
Result := Origin.Count
|
||||
else
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
function TSortedChartSource.GetItem(AIndex: Integer): PChartDataItem;
|
||||
begin
|
||||
if Origin <> nil then
|
||||
Result := PChartDataItem(Origin.Item[PInteger(FData.Items[AIndex])^])
|
||||
else
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
procedure TSortedChartSource.ResetTransformation(ACount: Integer);
|
||||
var
|
||||
i: Integer;
|
||||
pint: PInteger;
|
||||
begin
|
||||
if ACount > FData.Count then begin
|
||||
for i := 0 to FData.Count - 1 do
|
||||
PInteger(FData.List^[i])^ := i;
|
||||
|
||||
FData.Capacity := ACount;
|
||||
|
||||
pint := nil;
|
||||
try // optimization: don't execute try..except..end in a loop
|
||||
for i := FData.Count to ACount - 1 do begin
|
||||
New(pint);
|
||||
pint^ := i;
|
||||
FData.Add(pint); // don't use ItemAdd() here
|
||||
pint := nil;
|
||||
end;
|
||||
except
|
||||
if pint <> nil then
|
||||
Dispose(pint);
|
||||
raise;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
for i := ACount to FData.Count - 1 do
|
||||
Dispose(PInteger(FData.List^[i]));
|
||||
|
||||
FData.Count := ACount;
|
||||
FData.Capacity := ACount; // release needless memory
|
||||
|
||||
for i := 0 to FData.Count - 1 do
|
||||
PInteger(FData.List^[i])^ := i;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSortedChartSource.SetOrigin(AValue: TCustomChartSource);
|
||||
begin
|
||||
if AValue = Self then
|
||||
AValue := nil;
|
||||
if FOrigin = AValue then exit;
|
||||
if FOrigin <> nil then
|
||||
FOrigin.Broadcaster.Unsubscribe(FListener);
|
||||
FOrigin := AValue;
|
||||
if FOrigin <> nil then
|
||||
FOrigin.Broadcaster.Subscribe(FListener);
|
||||
Changed(nil);
|
||||
end;
|
||||
|
||||
procedure TSortedChartSource.SetXCount(AValue: Cardinal);
|
||||
begin
|
||||
Unused(AValue);
|
||||
raise EXCountError.Create('Cannot set XCount');
|
||||
end;
|
||||
|
||||
procedure TSortedChartSource.SetYCount(AValue: Cardinal);
|
||||
begin
|
||||
Unused(AValue);
|
||||
raise EYCountError.Create('Cannot set YCount');
|
||||
end;
|
||||
|
||||
function TSortedChartSource.BasicExtent: TDoubleRect;
|
||||
begin
|
||||
if Origin = nil then
|
||||
Result := EmptyExtent
|
||||
else
|
||||
Result := Origin.BasicExtent;
|
||||
end;
|
||||
|
||||
function TSortedChartSource.Extent: TDoubleRect;
|
||||
begin
|
||||
if Origin = nil then
|
||||
Result := EmptyExtent
|
||||
else
|
||||
Result := Origin.Extent;
|
||||
end;
|
||||
|
||||
function TSortedChartSource.ExtentCumulative: TDoubleRect;
|
||||
begin
|
||||
if Origin = nil then
|
||||
Result := EmptyExtent
|
||||
else
|
||||
Result := Origin.ExtentCumulative;
|
||||
end;
|
||||
|
||||
function TSortedChartSource.ExtentList: TDoubleRect;
|
||||
begin
|
||||
if Origin = nil then
|
||||
Result := EmptyExtent
|
||||
else
|
||||
Result := Origin.ExtentList;
|
||||
end;
|
||||
|
||||
function TSortedChartSource.ExtentXYList: TDoubleRect;
|
||||
begin
|
||||
if Origin = nil then
|
||||
Result := EmptyExtent
|
||||
else
|
||||
Result := Origin.ExtentXYList;
|
||||
end;
|
||||
|
||||
function TSortedChartSource.ValuesTotal: Double;
|
||||
begin
|
||||
if Origin = nil then
|
||||
Result := 0
|
||||
else
|
||||
Result := Origin.ValuesTotal;
|
||||
end;
|
||||
|
||||
{ TMWCRandomGenerator }
|
||||
|
||||
function TMWCRandomGenerator.Get: LongWord;
|
||||
@ -1452,7 +1675,7 @@ end;
|
||||
procedure TCalculatedChartSource.SetOrigin(AValue: TCustomChartSource);
|
||||
begin
|
||||
if AValue = Self then
|
||||
AValue := nil;
|
||||
AValue := nil;
|
||||
if FOrigin = AValue then exit;
|
||||
if FOrigin <> nil then
|
||||
FOrigin.Broadcaster.Unsubscribe(FListener);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user