mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-11-04 11:24:40 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			612 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			612 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
{
 | 
						|
 *****************************************************************************
 | 
						|
  See the file COPYING.modifiedLGPL.txt, included in this distribution,
 | 
						|
  for details about the license.
 | 
						|
 *****************************************************************************
 | 
						|
 | 
						|
  Authors: Werner Pamler, Alexander Klenin
 | 
						|
 | 
						|
  Usage:
 | 
						|
  - Add the ChartListbox to a form with a TChart
 | 
						|
  - Connect the chart to the listbox by setting the chart property of the
 | 
						|
    TChartlistbox. The ChartListbox will be populated with the series in the
 | 
						|
    chart and will automatically track changes of the series.
 | 
						|
  - Check/uncheck series in the ChartListbox to show/hide them in the chart.
 | 
						|
    As usual, checking is done by mouse clicks or by pressing the space bar.
 | 
						|
  - Play with the properties to modify the standard behavior, e.g. set
 | 
						|
    CheckStyle to cbsRadioButton in order to get a radiobutton behavior, i.e. to
 | 
						|
    allow only one visible series.
 | 
						|
}
 | 
						|
 | 
						|
unit TAChartListbox;
 | 
						|
 | 
						|
{$mode objfpc}{$H+}
 | 
						|
 | 
						|
interface
 | 
						|
 | 
						|
uses
 | 
						|
  Classes, Controls, StdCtrls,
 | 
						|
  TAChartUtils, TACustomSeries, TALegend, TAGraph;
 | 
						|
 | 
						|
type
 | 
						|
  TChartListbox = class;
 | 
						|
 | 
						|
  TChartListboxIndexEvent = procedure (
 | 
						|
    ASender: TObject; AIndex: Integer) of object;
 | 
						|
 | 
						|
  TChartListboxAddSeriesEvent = procedure (
 | 
						|
    ASender: TChartListbox; ASeries: TCustomChartSeries;
 | 
						|
    AItems: TChartLegendItems; var ASkip: Boolean
 | 
						|
  ) of object;
 | 
						|
 | 
						|
  TCheckBoxesStyle = (cbsCheckbox, cbsRadiobutton);
 | 
						|
 | 
						|
  TChartListOption = (cloShowCheckboxes, cloShowIcons, cloRefreshOnSourceChange);
 | 
						|
  TChartListOptions = set of TChartListOption;
 | 
						|
 | 
						|
const
 | 
						|
  SHOW_ALL = [cloShowCheckboxes, cloShowIcons];
 | 
						|
 | 
						|
type
 | 
						|
  TChartListbox = class(TCustomListbox)
 | 
						|
  private
 | 
						|
    FChart: TChart;
 | 
						|
    FCheckStyle: TCheckBoxesStyle;
 | 
						|
    FLegendItems: TChartLegendItems;
 | 
						|
    FListener: TListener;
 | 
						|
    FOnAddSeries: TChartListboxAddSeriesEvent;
 | 
						|
    FOnCheckboxClick: TChartListboxIndexEvent;
 | 
						|
    FOnItemClick: TChartListboxIndexEvent;
 | 
						|
    FOnPopulate: TNotifyEvent;
 | 
						|
    FOnSeriesIconDblClick: TChartListboxIndexEvent;
 | 
						|
    FOptions: TChartListOptions;
 | 
						|
    FSeriesIconClicked: Integer;
 | 
						|
    function GetChecked(AIndex: Integer): Boolean;
 | 
						|
    function GetSeries(AIndex: Integer): TCustomChartSeries;
 | 
						|
    function GetSeriesCount: Integer;
 | 
						|
    procedure EnsureSingleChecked(AIndex: Integer = -1);
 | 
						|
    procedure SetChart(AValue: TChart);
 | 
						|
    procedure SetChecked(AIndex: Integer; AValue: Boolean);
 | 
						|
    procedure SetCheckStyle(AValue: TCheckBoxesStyle);
 | 
						|
    procedure SetOnAddSeries(AValue: TChartListboxAddSeriesEvent);
 | 
						|
    procedure SetOnPopulate(AValue: TNotifyEvent);
 | 
						|
    procedure SetOptions(AValue: TChartListOptions);
 | 
						|
 | 
						|
  protected
 | 
						|
    procedure DblClick; override;
 | 
						|
    procedure DrawItem(
 | 
						|
      AIndex: Integer; ARect: TRect; AState: TOwnerDrawState); override;
 | 
						|
    procedure KeyDown(var AKey: Word; AShift: TShiftState); override;
 | 
						|
    procedure MouseDown(
 | 
						|
      AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer); override;
 | 
						|
  protected
 | 
						|
    procedure CalcRects(
 | 
						|
      const AItemRect: TRect; out ACheckboxRect, ASeriesIconRect: TRect);
 | 
						|
    procedure ClickedCheckbox(AIndex: Integer); virtual;
 | 
						|
    procedure ClickedItem(AIndex: Integer); virtual;
 | 
						|
    procedure ClickedSeriesIcon(AIndex: Integer); virtual;
 | 
						|
    function CreateLegendItems: TChartLegendItems;
 | 
						|
    procedure Notification(AComponent: TComponent; AOperation: TOperation); override;
 | 
						|
    procedure Populate;
 | 
						|
 | 
						|
  public
 | 
						|
    constructor Create(AOwner: TComponent); override;
 | 
						|
    destructor Destroy; override;
 | 
						|
    function FindSeriesIndex(ASeries: TCustomChartSeries): Integer;
 | 
						|
    procedure MeasureItem(AIndex: Integer; var AHeight: Integer); override;
 | 
						|
    procedure RemoveSeries(ASeries: TCustomChartSeries);
 | 
						|
    procedure SeriesChanged(ASender: TObject);
 | 
						|
 | 
						|
    property Checked[AIndex: Integer]: Boolean read GetChecked write SetChecked;
 | 
						|
    property Series[AIndex: Integer]: TCustomChartSeries read GetSeries;
 | 
						|
    property SeriesCount: Integer read GetSeriesCount;
 | 
						|
 | 
						|
  published
 | 
						|
    property Chart: TChart read FChart write SetChart;
 | 
						|
    property CheckStyle: TCheckBoxesStyle
 | 
						|
      read FCheckStyle write SetCheckStyle default cbsCheckbox;
 | 
						|
    property Options: TChartListOptions
 | 
						|
      read FOptions write SetOptions default SHOW_ALL;
 | 
						|
  published
 | 
						|
    property OnAddSeries: TChartListboxAddSeriesEvent
 | 
						|
      read FOnAddSeries write SetOnAddSeries;
 | 
						|
    property OnCheckboxClick: TChartListboxIndexEvent
 | 
						|
      read FOnCheckboxClick write FOnCheckboxClick;
 | 
						|
    property OnItemClick: TChartListboxIndexEvent
 | 
						|
      read FOnItemClick write FOnItemClick;
 | 
						|
    property OnPopulate: TNotifyEvent read FOnPopulate write SetOnPopulate;
 | 
						|
    property OnSeriesIconDblClick: TChartListboxIndexEvent
 | 
						|
      read FOnSeriesIconDblClick write FOnSeriesIconDblClick;
 | 
						|
  published
 | 
						|
    property Align;
 | 
						|
//    property AllowGrayed;
 | 
						|
    property Anchors;
 | 
						|
    property BidiMode;
 | 
						|
    property BorderSpacing;
 | 
						|
    property BorderStyle;
 | 
						|
    property Color;
 | 
						|
    property Columns;
 | 
						|
    property Constraints;
 | 
						|
    property DragCursor;
 | 
						|
    property DragMode;
 | 
						|
    property Enabled;
 | 
						|
    property ExtendedSelect;
 | 
						|
    property Font;
 | 
						|
    property IntegralHeight;
 | 
						|
//    property Items;
 | 
						|
    property ItemHeight;
 | 
						|
    property MultiSelect;
 | 
						|
    property OnChangeBounds;
 | 
						|
    property OnClick;
 | 
						|
//    property OnClickCheck;
 | 
						|
    property OnContextPopup;
 | 
						|
    property OnDblClick;
 | 
						|
    property OnDragDrop;
 | 
						|
    property OnDragOver;
 | 
						|
    property OnDrawItem;
 | 
						|
    property OnEndDrag;
 | 
						|
    property OnEnter;
 | 
						|
    property OnExit;
 | 
						|
//    property OnItemClick;
 | 
						|
    property OnKeyDown;
 | 
						|
    property OnKeyPress;
 | 
						|
    property OnKeyUp;
 | 
						|
    property OnMouseDown;
 | 
						|
    property OnMouseEnter;
 | 
						|
    property OnMouseLeave;
 | 
						|
    property OnMouseMove;
 | 
						|
    property OnMouseUp;
 | 
						|
    property OnMouseWheel;
 | 
						|
    property OnMouseWheelDown;
 | 
						|
    property OnMouseWheelUp;
 | 
						|
    property OnResize;
 | 
						|
    property OnShowHint;
 | 
						|
    property OnStartDrag;
 | 
						|
    property OnUTF8KeyPress;
 | 
						|
    property ParentBidiMode;
 | 
						|
    property ParentFont;
 | 
						|
    property ParentShowHint;
 | 
						|
    property PopupMenu;
 | 
						|
    property ShowHint;
 | 
						|
    property Sorted;
 | 
						|
//    property Style;
 | 
						|
    property TabOrder;
 | 
						|
    property TabStop;
 | 
						|
    property TopIndex;
 | 
						|
    property Visible;
 | 
						|
  end;
 | 
						|
 | 
						|
procedure Register;
 | 
						|
 | 
						|
 | 
						|
implementation
 | 
						|
 | 
						|
uses
 | 
						|
  Graphics, Math, LCLIntf, LCLType, SysUtils, Themes,
 | 
						|
  TACustomSource, TADrawerCanvas, TADrawUtils, TAEnumerators, TAGeometry;
 | 
						|
 | 
						|
procedure Register;
 | 
						|
begin
 | 
						|
  RegisterComponents(CHART_COMPONENT_IDE_PAGE, [TChartListbox]);
 | 
						|
end;
 | 
						|
 | 
						|
{ TChartListbox }
 | 
						|
 | 
						|
constructor TChartListbox.Create(AOwner: TComponent);
 | 
						|
begin
 | 
						|
  inherited Create(AOwner);
 | 
						|
  Style := lbOwnerDrawFixed;
 | 
						|
  FListener := TListener.Create(@FChart, @SeriesChanged);
 | 
						|
  FOptions := SHOW_ALL;
 | 
						|
end;
 | 
						|
 | 
						|
destructor TChartListbox.Destroy;
 | 
						|
begin
 | 
						|
  FreeAndNil(FListener);
 | 
						|
  FreeAndNil(FLegendItems);
 | 
						|
  inherited;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.CalcRects(
 | 
						|
  const AItemRect: TRect; out ACheckboxRect, ASeriesIconRect: TRect);
 | 
						|
{ based on the rect of a listbox item, calculates the locations of the
 | 
						|
  checkbox and of the series icon }
 | 
						|
var
 | 
						|
  w, x: Integer;
 | 
						|
begin
 | 
						|
  ACheckBoxRect := ZeroRect;
 | 
						|
  ASeriesIconRect := ZeroRect;
 | 
						|
 | 
						|
  w := Canvas.TextHeight('Tg');
 | 
						|
  x := 4;
 | 
						|
  if cloShowCheckboxes in Options then begin
 | 
						|
    with AItemRect do
 | 
						|
      ACheckboxRect := Bounds(Left + 4, (Top + Bottom - w) div 2, w, w);
 | 
						|
    if cloShowIcons in Options then
 | 
						|
      x += ACheckboxRect.Right;
 | 
						|
  end
 | 
						|
  else begin
 | 
						|
    if cloShowIcons in Options then
 | 
						|
      x += AItemRect.Left;
 | 
						|
  end;
 | 
						|
  if cloShowIcons in Options then
 | 
						|
    ASeriesIconRect := Rect(
 | 
						|
      x, AItemRect.Top + 2, x + FChart.Legend.SymbolWidth, AItemRect.Bottom - 2);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.ClickedCheckbox(AIndex: Integer);
 | 
						|
begin
 | 
						|
  Checked[AIndex] := not Checked[AIndex];
 | 
						|
  if Assigned(OnCheckboxClick) then
 | 
						|
    OnCheckboxClick(Self, AIndex);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.ClickedItem(AIndex: Integer);
 | 
						|
begin
 | 
						|
  if Assigned(OnItemClick) then
 | 
						|
    OnItemClick(Self, AIndex);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.ClickedSeriesIcon(AIndex: Integer);
 | 
						|
begin
 | 
						|
  if Assigned(OnSeriesIconDblClick) then
 | 
						|
    OnSeriesIconDblClick(Self, AIndex);
 | 
						|
end;
 | 
						|
 | 
						|
function TChartListbox.CreateLegendItems: TChartLegendItems;
 | 
						|
{ creates the a TLegendItems list and populates it with information for
 | 
						|
  all series contained in the Chart. In case of MultiLegend items, only
 | 
						|
  a single legend item is used }
 | 
						|
var
 | 
						|
  skip: Boolean;
 | 
						|
  s: TCustomChartSeries;
 | 
						|
begin
 | 
						|
  Result := TChartLegendItems.Create;
 | 
						|
  try
 | 
						|
    if FChart = nil then exit;
 | 
						|
    for s in CustomSeries(Chart) do begin
 | 
						|
      if Assigned(OnAddSeries) then begin
 | 
						|
        skip := false;
 | 
						|
        FOnAddSeries(Self, s, Result, skip);
 | 
						|
        if skip then continue;
 | 
						|
      end;
 | 
						|
      s.GetSingleLegendItem(Result);
 | 
						|
    end;
 | 
						|
  except
 | 
						|
    FreeAndNil(Result);
 | 
						|
    raise;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.DblClick;
 | 
						|
begin
 | 
						|
  inherited DblClick;
 | 
						|
  if FSeriesIconClicked <> -1 then
 | 
						|
    ClickedSeriesIcon(FSeriesIconClicked);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.DrawItem(
 | 
						|
  AIndex: Integer; ARect: TRect; AState: TOwnerDrawState);
 | 
						|
{ draws the listbox item }
 | 
						|
const
 | 
						|
  UNTHEMED_FLAGS: array [TCheckboxesStyle, Boolean] of Integer = (
 | 
						|
    (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED),
 | 
						|
    (DFCS_BUTTONRADIO, DFCS_BUTTONRADIO or DFCS_CHECKED)
 | 
						|
  );
 | 
						|
  THEMED_FLAGS: array [TCheckboxesStyle, Boolean] of TThemedButton = (
 | 
						|
    (tbCheckBoxUncheckedNormal, tbCheckBoxCheckedNormal),
 | 
						|
    (tbRadioButtonUnCheckedNormal, tbRadioButtonCheckedNormal)
 | 
						|
  );
 | 
						|
var
 | 
						|
  id: IChartDrawer;
 | 
						|
  rcb, ricon: TRect;
 | 
						|
  te: TThemedElementDetails;
 | 
						|
  x: Integer;
 | 
						|
  ch: Boolean;
 | 
						|
begin
 | 
						|
  if Assigned(OnDrawItem) then begin
 | 
						|
    OnDrawItem(Self, AIndex, ARect, AState);
 | 
						|
    exit;
 | 
						|
  end;
 | 
						|
  if (FChart = nil) or not InRange(AIndex, 0, Count - 1) then
 | 
						|
    exit;
 | 
						|
 | 
						|
  Canvas.FillRect(ARect);
 | 
						|
  CalcRects(ARect, rcb, ricon);
 | 
						|
 | 
						|
  if cloShowCheckboxes in Options then begin
 | 
						|
    ch := Checked[AIndex];
 | 
						|
    if ThemeServices.ThemesEnabled then begin
 | 
						|
      te := ThemeServices.GetElementDetails(THEMED_FLAGS[FCheckStyle, ch]);
 | 
						|
      ThemeServices.DrawElement(Canvas.Handle, te, rcb);
 | 
						|
    end
 | 
						|
    else
 | 
						|
      DrawFrameControl(
 | 
						|
        Canvas.Handle, rcb, DFC_BUTTON, UNTHEMED_FLAGS[FCheckStyle, ch]);
 | 
						|
    x := rcb.Right;
 | 
						|
  end
 | 
						|
  else
 | 
						|
    x := ARect.Left;
 | 
						|
 | 
						|
  Canvas.Brush.Style := bsClear;
 | 
						|
  if cloShowIcons in Options then begin
 | 
						|
    id := TCanvasDrawer.Create(Canvas);
 | 
						|
    id.Pen := Chart.Legend.SymbolFrame;
 | 
						|
    FLegendItems[AIndex].Draw(id, ricon);
 | 
						|
  end
 | 
						|
  else
 | 
						|
    Canvas.TextOut(x + 2, ARect.Top, FLegendItems.Items[AIndex].Text);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.EnsureSingleChecked(AIndex: Integer);
 | 
						|
var
 | 
						|
  i: Integer;
 | 
						|
  ser: TCustomChartSeries;
 | 
						|
begin
 | 
						|
  if (FCheckStyle <> cbsRadioButton) or not (cloShowCheckboxes in Options) then
 | 
						|
    exit;
 | 
						|
  FListener.OnNotify := nil;
 | 
						|
  try
 | 
						|
    for i := 0 to FLegendItems.Count - 1 do begin
 | 
						|
      ser := GetSeries(i);
 | 
						|
      if ser = nil then continue;
 | 
						|
      if (AIndex < 0) and ser.Active then
 | 
						|
        AIndex := i
 | 
						|
      else
 | 
						|
        ser.Active := AIndex = i;
 | 
						|
    end;
 | 
						|
  finally
 | 
						|
    FListener.OnNotify := @SeriesChanged;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
function TChartListbox.FindSeriesIndex(ASeries: TCustomChartSeries): Integer;
 | 
						|
{ searches the internal legend items list for the specified series }
 | 
						|
begin
 | 
						|
  for Result := 0 to FLegendItems.Count - 1 do
 | 
						|
    if GetSeries(Result) = ASeries then exit;
 | 
						|
  Result := -1;
 | 
						|
end;
 | 
						|
 | 
						|
function TChartListbox.GetChecked(AIndex: Integer): Boolean;
 | 
						|
{ report the checked status. This is determined by the visibility of the
 | 
						|
  series with the given index. }
 | 
						|
var
 | 
						|
  ser: TBasicChartSeries;
 | 
						|
begin
 | 
						|
  ser := GetSeries(AIndex);
 | 
						|
  Result := (ser <> nil) and ser.Active;
 | 
						|
end;
 | 
						|
 | 
						|
function TChartListbox.GetSeries(AIndex: Integer): TCustomChartSeries;
 | 
						|
{ extracts, for the given index, the series from the internal
 | 
						|
  legend items list. }
 | 
						|
var
 | 
						|
  legitem: TLegendItem;
 | 
						|
begin
 | 
						|
  legitem := FLegendItems[AIndex];
 | 
						|
  if (legitem <> nil) and (legitem.Owner is TCustomChartSeries) then
 | 
						|
    Result := TCustomChartSeries(legitem.Owner)
 | 
						|
  else
 | 
						|
    Result := nil;
 | 
						|
end;
 | 
						|
 | 
						|
function TChartListbox.GetSeriesCount : Integer;
 | 
						|
{ determines the number of series displayed in the listbox.
 | 
						|
  Note that this may be different from the Chart.SeriesCount if
 | 
						|
  RemoveSeries has been called }
 | 
						|
begin
 | 
						|
  Result := FLegendItems.Count;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.KeyDown(var AKey: Word; AShift: TShiftState);
 | 
						|
{ allows checking/unchecking of items by means of pressing the space bar }
 | 
						|
begin
 | 
						|
  if
 | 
						|
    (AKey = VK_SPACE) and (AShift = []) and
 | 
						|
    (cloShowCheckboxes in Options) and (ItemIndex >= 0)
 | 
						|
  then begin
 | 
						|
    ClickedCheckbox(ItemIndex);
 | 
						|
    AKey := VK_UNKNOWN;
 | 
						|
  end
 | 
						|
  else
 | 
						|
    inherited KeyDown(AKey, AShift);
 | 
						|
end;
 | 
						|
 | 
						|
// Item height is determined as maximum of:
 | 
						|
// checkbox height, text height, ItemHeight property value.
 | 
						|
procedure TChartListbox.MeasureItem(AIndex: Integer; var AHeight: Integer);
 | 
						|
begin
 | 
						|
  inherited MeasureItem(AIndex, AHeight);
 | 
						|
  AHeight := Max(CalculateStandardItemHeight, AHeight);
 | 
						|
  if cloShowCheckboxes in Options then
 | 
						|
    AHeight := Max(AHeight, GetSystemMetrics(SM_CYMENUCHECK) + 2);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.MouseDown(
 | 
						|
  AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer);
 | 
						|
{ standard MouseDown handler: checks if the click occured on the checkbox,
 | 
						|
  on the series icon, or on the text.
 | 
						|
  The visibility state of the item's series is changed when clicking on the
 | 
						|
  checkbox, and an event OnCheckboxClick is generated.
 | 
						|
  An event OnSeriesIconClick is generated when double-clicking on the
 | 
						|
  series icon; the method stores the series list index here.
 | 
						|
  An event OnItemClick is generated when the click occured neither on the
 | 
						|
  checkbox nor the series icon.
 | 
						|
}
 | 
						|
var
 | 
						|
  rcb, ricon: TRect;
 | 
						|
  index: Integer;
 | 
						|
  p: TPoint;
 | 
						|
begin
 | 
						|
  FSeriesIconClicked := -1;
 | 
						|
  try
 | 
						|
    if AButton <> mbLeft then exit;
 | 
						|
    p := Point(AX, AY);
 | 
						|
    index := GetIndexAtXY(AX, AY);
 | 
						|
    if index < 0 then exit;
 | 
						|
    CalcRects(ItemRect(index), rcb, ricon);
 | 
						|
    if (cloShowCheckboxes in Options) and IsPointInRect(p, rcb) then
 | 
						|
      ClickedCheckbox(index)
 | 
						|
    else if (cloShowIcons in Options) and IsPointInRect(p, ricon) then
 | 
						|
      // Remember clicked index for the double click event.
 | 
						|
      FSeriesIconClicked := index
 | 
						|
    else
 | 
						|
      ClickedItem(index);
 | 
						|
  finally
 | 
						|
    inherited MouseDown(AButton, AShift, AX, AY);
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.Notification(AComponent: TComponent; AOperation: TOperation);
 | 
						|
begin
 | 
						|
  if (AOperation = opRemove) and (AComponent = FChart) then
 | 
						|
    FChart := nil;
 | 
						|
  inherited Notification(AComponent, AOperation);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.Populate;
 | 
						|
{ populates the listbox with all series contained in the chart. Use the event
 | 
						|
  OnPopulate if you don't omit special series from the listbox (RemoveSeries) }
 | 
						|
var
 | 
						|
  li: TLegendItem;
 | 
						|
  list: TFPList;
 | 
						|
  ser: TCustomChartSeries;
 | 
						|
  i, idx: Integer;
 | 
						|
begin
 | 
						|
  Items.BeginUpdate;
 | 
						|
  list := TFPList.Create;
 | 
						|
  try
 | 
						|
    // In case of multiselect, the selected items would get lost here.
 | 
						|
    // Store series belonging to selected items in temporary list
 | 
						|
    for i:=0 to Items.Count-1 do
 | 
						|
      if Selected[i] then begin
 | 
						|
        li := TLegendItem(Items.Objects[i]);
 | 
						|
        if (li <> nil) and (li.Owner is TCustomChartSeries) then begin
 | 
						|
          ser := TCustomChartSeries(li.Owner);
 | 
						|
          list.Add(ser);
 | 
						|
        end;
 | 
						|
      end;
 | 
						|
 | 
						|
    Items.Clear;
 | 
						|
    if (FChart = nil) or (FChart.Series = nil) then exit;
 | 
						|
    FreeAndNil(FLegendItems);
 | 
						|
    FLegendItems := CreateLegendItems;
 | 
						|
    Chart.Legend.SortItemsByOrder(FLegendItems);
 | 
						|
    for li in FLegendItems do begin
 | 
						|
      // The caption is owner-drawn, but add it anyway for user convenience.
 | 
						|
      idx := Items.AddObject(li.Text, li);
 | 
						|
      // Restore selected state from temporary list
 | 
						|
      if (li.Owner is TCustomChartSeries) then begin
 | 
						|
        ser := TCustomChartSeries(li.Owner);
 | 
						|
        if list.IndexOf(ser) <> -1 then Selected[idx] := true;
 | 
						|
      end;
 | 
						|
    end;
 | 
						|
    if Assigned(OnPopulate) then
 | 
						|
      OnPopulate(Self);
 | 
						|
  finally
 | 
						|
    list.Free;
 | 
						|
    Items.EndUpdate;
 | 
						|
  end;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.RemoveSeries(ASeries: TCustomChartSeries);
 | 
						|
{ removes the series from the listbox, but keeps it in the chart }
 | 
						|
var
 | 
						|
  index: Integer;
 | 
						|
begin
 | 
						|
  index := FindSeriesIndex(ASeries);
 | 
						|
  if index = -1 then exit;
 | 
						|
  FLegendItems.Delete(index);
 | 
						|
  Items.Delete(index);
 | 
						|
  Invalidate;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.SeriesChanged(ASender: TObject);
 | 
						|
{ Notification procedure of the listener. Responds to chart broadcasts
 | 
						|
  by populating the listbox with the chart's series }
 | 
						|
begin
 | 
						|
  if
 | 
						|
    (ASender is TCustomChartSource) and
 | 
						|
    not (cloRefreshOnSourceChange in Options)
 | 
						|
  then
 | 
						|
    exit;
 | 
						|
  Populate;
 | 
						|
  { in case of radiobutton mode, it is necessary to uncheck the other
 | 
						|
    series; there can be only one active series in this mode }
 | 
						|
  if
 | 
						|
    (ASender is TCustomChartSeries) and (ASender as TCustomChartSeries).Active
 | 
						|
  then
 | 
						|
    EnsureSingleChecked(FindSeriesIndex(ASender as TCustomChartSeries))
 | 
						|
  else
 | 
						|
    EnsureSingleChecked;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.SetChart(AValue: TChart);
 | 
						|
{ connects the ListBox to the chart }
 | 
						|
begin
 | 
						|
  if FChart = AValue then exit;
 | 
						|
 | 
						|
  if FListener.IsListening then
 | 
						|
    FChart.Broadcaster.Unsubscribe(FListener);
 | 
						|
  FChart := AValue;
 | 
						|
  if FChart <> nil then
 | 
						|
    FChart.Broadcaster.Subscribe(FListener);
 | 
						|
  SeriesChanged(Self);
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.SetChecked(AIndex: Integer; AValue: Boolean);
 | 
						|
{ shows/hides the series with the specified index of its listbox item.
 | 
						|
  In case of radiobutton style, all other series are hidden if AValue=true }
 | 
						|
var
 | 
						|
  ser: TCustomChartSeries;
 | 
						|
begin
 | 
						|
  ser := GetSeries(AIndex);
 | 
						|
  if (ser = nil) or (ser.Active = AValue) then exit;
 | 
						|
  // Do not listen to this change since we know what changed.
 | 
						|
  FListener.OnNotify := nil;
 | 
						|
  try
 | 
						|
    ser.Active := AValue;
 | 
						|
  finally
 | 
						|
    FListener.OnNotify := @SeriesChanged;
 | 
						|
  end;
 | 
						|
  if AValue then
 | 
						|
    EnsureSingleChecked(FindSeriesIndex(ser));
 | 
						|
  Invalidate;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.SetCheckStyle(AValue: TCheckBoxesStyle);
 | 
						|
{ selects "checkbox" or "radiobutton" styles. In radiobutton mode, only
 | 
						|
  one series can be visible }
 | 
						|
begin
 | 
						|
  if FCheckStyle = AValue then exit;
 | 
						|
  FCheckStyle := AValue;
 | 
						|
  EnsureSingleChecked;
 | 
						|
  Invalidate;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.SetOnAddSeries(AValue: TChartListboxAddSeriesEvent);
 | 
						|
begin
 | 
						|
  if TMethod(FOnAddSeries) = TMethod(AValue) then exit;
 | 
						|
  FOnAddSeries := AValue;
 | 
						|
  Populate;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.SetOnPopulate(AValue: TNotifyEvent);
 | 
						|
begin
 | 
						|
  if TMethod(FOnPopulate) = TMethod(AValue) then exit;
 | 
						|
  FOnPopulate := AValue;
 | 
						|
  Populate;
 | 
						|
end;
 | 
						|
 | 
						|
procedure TChartListbox.SetOptions(AValue: TChartListOptions);
 | 
						|
begin
 | 
						|
  if FOptions = AValue then exit;
 | 
						|
  FOptions := AValue;
 | 
						|
  EnsureSingleChecked;
 | 
						|
  Invalidate;
 | 
						|
end;
 | 
						|
 | 
						|
end.
 | 
						|
 |