TAChart: Convert TChartListBox code to standard formatting

git-svn-id: trunk@31632 -
This commit is contained in:
ask 2011-07-10 02:01:00 +00:00
parent c7b8797cf5
commit 77e81e6501

View File

@ -24,7 +24,7 @@ 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 und will automatically track changes of the series.
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
@ -43,78 +43,82 @@ uses
TAChartUtils, TACustomSeries, TALegend, TAGraph;
type
TChartListboxIndexEvent = procedure(Sender:TObject; Index:integer) of object;
TChartListboxIndexEvent = procedure (
Sender: TObject; Index: Integer) of object;
TCheckBoxesStyle = (cbsCheckbox, cbsRadiobutton);
TChartListbox = class(TCustomListbox)
private
FChart : TChart;
FListener : TListener;
FLegendItems : TChartLegendItems;
FShowSeriesIcons : boolean;
FShowCheckboxes : boolean;
FCheckStyle : TCheckBoxesStyle;
FLockCount : integer;
FSeriesIconClicked : integer;
FOnCheckboxClick : TChartListboxIndexEvent;
FOnSeriesIconClick : TChartListboxIndexEvent;
FOnItemClick : TChartListboxIndexEvent;
FOnPopulate : TNotifyEvent;
function GetChecked(AIndex:integer) : boolean;
function GetLegendItem(AIndex:integer) : TLegendItem;
function GetSeries(AIndex:integer) : TCustomChartSeries;
function GetSeriesCount : integer;
procedure SeriesChanged(ASender:TObject);
procedure SetChart(AValue:TChart);
procedure SetChecked(AIndex:integer; AValue:boolean);
procedure SetCheckStyle(AValue:TCheckBoxesStyle);
procedure SetShowCheckboxes(AValue:boolean);
procedure SetShowSeriesIcons(AValue:boolean);
FChart: TChart;
FListener: TListener;
FLegendItems: TChartLegendItems;
FShowSeriesIcons: Boolean;
FShowCheckboxes: Boolean;
FCheckStyle: TCheckBoxesStyle;
FLockCount: Integer;
FSeriesIconClicked: Integer;
FOnCheckboxClick: TChartListboxIndexEvent;
FOnSeriesIconClick: TChartListboxIndexEvent;
FOnItemClick: TChartListboxIndexEvent;
FOnPopulate: TNotifyEvent;
function GetChecked(AIndex: Integer): Boolean;
function GetLegendItem(AIndex: Integer): TLegendItem;
function GetSeries(AIndex: Integer): TCustomChartSeries;
function GetSeriesCount: Integer;
procedure SeriesChanged(ASender: TObject);
procedure SetChart(AValue: TChart);
procedure SetChecked(AIndex: Integer; AValue: Boolean);
procedure SetCheckStyle(AValue: TCheckBoxesStyle);
procedure SetShowCheckboxes(AValue: Boolean);
procedure SetShowSeriesIcons(AValue: Boolean);
protected
procedure CalcRects(const AItemRect:TRect; out CheckboxRect,SeriesIconRect: TRect);
procedure ClickedCheckbox(AIndex:integer); virtual;
procedure ClickedItem(AIndex:integer); virtual;
procedure ClickedSeriesIcon(AIndex:integer); virtual;
function CreateLegendItems : TChartLegendItems;
procedure CalcRects(
const AItemRect: TRect; out CheckboxRect, SeriesIconRect: TRect);
procedure ClickedCheckbox(AIndex: Integer); virtual;
procedure ClickedItem(AIndex: Integer); virtual;
procedure ClickedSeriesIcon(AIndex: Integer); virtual;
function CreateLegendItems: TChartLegendItems;
procedure DblClick; override;
procedure DrawItem(Index: Integer; ARect: TRect; AState: TOwnerDrawState); override;
function FirstCheckedIndex : integer;
function IsLocked : boolean;
procedure DrawItem(
Index: Integer; ARect: TRect; AState: TOwnerDrawState); override;
function FirstCheckedIndex: Integer;
function IsLocked: Boolean;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure Lock;
procedure MouseDown(AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer); override;
procedure Notification(AComponent: TComponent; AOperation: TOperation); override;
procedure MouseDown(
AButton: TMouseButton; AShift: TShiftState; AX, AY: Integer); override;
procedure Notification(
AComponent: TComponent; AOperation: TOperation); override;
procedure Populate;
procedure Unlock;
public
constructor Create(AOwner:TComponent); override;
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function FindSeriesIndex(ASeries:TCustomChartSeries) : integer;
function FindSeriesIndex(ASeries: TCustomChartSeries): Integer;
procedure MeasureItem(Index: Integer; var AHeight: Integer); override;
procedure RemoveSeries(ASeries:TCustomChartSeries);
property Checked[AIndex:integer] : boolean read GetChecked write SetChecked;
property Series[AIndex:integer] : TCustomChartSeries read GetSeries;
property SeriesCount : integer read GetSeriesCount;
procedure RemoveSeries(ASeries: TCustomChartSeries);
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
property Chart: TChart read FChart write SetChart;
property CheckStyle: TCheckBoxesStyle
read FCheckStyle write SetCheckStyle default cbsCheckbox;
property ShowCheckboxes : boolean
property ShowCheckboxes: Boolean
read FShowCheckboxes write SetShowCheckboxes default true;
property ShowSeriesIcons : boolean
property ShowSeriesIcons: Boolean
read FShowSeriesIcons write SetShowSeriesIcons default true;
property OnCheckboxClick : TChartListboxIndexEvent
property OnCheckboxClick: TChartListboxIndexEvent
read FOnCheckboxClick write FOnCheckboxClick;
property OnItemClick : TChartListboxIndexEvent
property OnItemClick: TChartListboxIndexEvent
read FOnItemClick write FOnItemClick;
property OnSeriesIconClick : TChartListboxIndexEvent
property OnSeriesIconClick: TChartListboxIndexEvent
read FOnSeriesIconClick write FOnSeriesIconClick;
property OnPopulate : TNotifyEvent
property OnPopulate: TNotifyEvent
read FOnPopulate write FOnPopulate;
property Align;
@ -181,14 +185,14 @@ procedure Register;
implementation
uses
math, LCLIntf, Themes, TAGeometry, TADrawUtils, TADrawerCanvas;
Math, LCLIntf, Themes, TAGeometry, TADrawUtils, TADrawerCanvas;
procedure Register;
begin
RegisterComponents(CHART_COMPONENT_IDE_PAGE, [TChartListbox]);
end;
constructor TChartListbox.Create(AOwner:TComponent);
constructor TChartListbox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Style := lbOwnerDrawFixed;
@ -204,63 +208,65 @@ begin
inherited Destroy;
end;
procedure TChartListbox.CalcRects(const AItemRect:TRect;
out CheckboxRect,SeriesIconRect: TRect);
procedure TChartListbox.CalcRects(
const AItemRect: TRect; out CheckboxRect, SeriesIconRect: TRect);
{ based on the rect of a listbox item, calculates the locations of the
checkbox and of the series icon }
var
w, x: integer;
w, x: Integer;
begin
CheckBoxRect := Rect(-1, -1, -1, -1);
SeriesIconRect := Rect(-1, -1, -1, -1);
w := GetSystemMetrics(SM_CYMENUCHECK);
x := 2;
if FShowCheckboxes then begin
CheckboxRect := Bounds(AItemRect.Left+1, AItemRect.Top+1, w, w);
CheckboxRect := Bounds(AItemRect.Left + 1, AItemRect.Top + 1, w, w);
if FShowSeriesIcons then
inc(x, CheckboxRect.Right);
end else begin
end
else begin
if FShowSeriesIcons then
inc(x, AItemRect.Left);
end;
if FShowSeriesIcons then
SeriesIconRect := Rect(x, AItemRect.Top+2, x+FChart.Legend.SymbolWidth, AItemRect.Bottom-2);
SeriesIconRect := Rect(
x, AItemRect.Top + 2, x + FChart.Legend.SymbolWidth, AItemRect.Bottom - 2);
end;
procedure TChartListbox.ClickedCheckbox(AIndex:integer);
procedure TChartListbox.ClickedCheckbox(AIndex: Integer);
begin
Checked[AIndex] := not Checked[AIndex];
if Assigned(OnCheckboxClick) then
OnCheckboxClick(self, AIndex);
OnCheckboxClick(Self, AIndex);
end;
procedure TChartListbox.ClickedItem(AIndex:integer);
procedure TChartListbox.ClickedItem(AIndex: Integer);
begin
if Assigned(OnItemClick) then
OnItemClick(self, AIndex);
OnItemClick(Self, AIndex);
end;
procedure TChartListbox.ClickedSeriesIcon(AIndex:integer);
procedure TChartListbox.ClickedSeriesIcon(AIndex: Integer);
begin
if Assigned(OnSeriesIconClick) then
OnSeriesIconClick(self, AIndex);
OnSeriesIconClick(Self, AIndex);
end;
function TChartListbox.CreateLegendItems : TChartLegendItems;
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
i : integer;
j : integer = MaxInt;
i: Integer;
j: Integer = MaxInt;
begin
result := TChartLegendItems.Create;
Result := TChartLegendItems.Create;
try
if FChart <> nil then
for i:=0 to FChart.SeriesCount-1 do begin
if (FChart.Series[i] is TCustomChartSeries) then begin
TCustomChartSeries(FChart.Series[i]).GetSingleLegendItem(result);
result[i].Owner := FChart.Series[i];
for i := 0 to FChart.SeriesCount - 1 do begin
if FChart.Series[i] is TCustomChartSeries then begin
TCustomChartSeries(FChart.Series[i]).GetSingleLegendItem(Result);
Result[i].Owner := FChart.Series[i];
end;
end;
for i := Result.Count - 1 downto 0 do
@ -277,30 +283,29 @@ end;
procedure TChartListbox.DblClick;
begin
inherited DblClick;
if (FSeriesIconClicked <> -1) then
if FSeriesIconClicked <> -1 then
ClickedSeriesIcon(FSeriesIconClicked);
end;
procedure TChartListbox.DrawItem(Index: Integer; ARect: TRect;
AState: TOwnerDrawState);
procedure TChartListbox.DrawItem(
Index: Integer; ARect: TRect; AState: TOwnerDrawState);
{ draws the listbox item }
const
IsChecked : array[TCheckboxesStyle, Boolean] of integer = (
IS_CHECKED: array[TCheckboxesStyle, Boolean] of Integer = (
(DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED),
(DFCS_BUTTONRADIO, DFCS_BUTTONRADIO or DFCS_CHECKED)
);
var
id : IChartDrawer;
Rcb, Ricon: TRect;
te : TThemedElementDetails;
x : integer;
id: IChartDrawer;
rcb, ricon: TRect;
te: TThemedElementDetails;
x: Integer;
begin
Unused(AState);
if FChart = nil then
exit;
if FChart = nil then exit;
CalcRects(ARect, Rcb, Ricon);
CalcRects(ARect, rcb, ricon);
if FShowCheckboxes then begin
if ThemeServices.ThemesEnabled then begin
@ -316,98 +321,102 @@ begin
else
te := ThemeServices.GetElementDetails(tbRadioButtonUnCheckedNormal);
end;
ThemeServices.DrawElement(Canvas.Handle, te, Rcb);
end else
DrawFrameControl(Canvas.Handle, Rcb, DFC_BUTTON, IsChecked[FCheckStyle, Checked[Index]]);
x := Rcb.Right;
end else
ThemeServices.DrawElement(Canvas.Handle, te, rcb);
end
else
DrawFrameControl(
Canvas.Handle, rcb, DFC_BUTTON, IS_CHECKED[FCheckStyle, Checked[Index]]);
x := rcb.Right;
end
else
x := ARect.Left;
if FShowSeriesIcons then begin
id := TCanvasDrawer.Create(Canvas);
id.Pen := Chart.Legend.SymbolFrame;
FLegendItems[Index].Draw(id, Ricon);
FLegendItems[Index].Draw(id, ricon);
end else
Canvas.TextOut(x+2, ARect.Top, FLegendItems.Items[Index].Text);
Canvas.TextOut(x + 2, ARect.Top, FLegendItems.Items[Index].Text);
end;
function TChartListbox.FindSeriesIndex(ASeries:TCustomChartSeries) : integer;
function TChartListbox.FindSeriesIndex(ASeries: TCustomChartSeries): Integer;
{ searches the internal legend items list for the specified series }
var
i : integer;
ser : TBasicChartSeries;
i: Integer;
ser: TBasicChartSeries;
begin
for i:=0 to FLegendItems.Count-1 do begin
for i := 0 to FLegendItems.Count - 1 do begin
ser := GetSeries(i);
if ser = ASeries then begin
result := i;
Result := i;
exit;
end;
end;
result := -1;
Result := -1;
end;
function TChartListbox.FirstCheckedIndex : integer;
function TChartListbox.FirstCheckedIndex: Integer;
{ Returns the index of the first listbox series that is active }
var
i : integer;
i: Integer;
begin
for i:=0 to FLegendItems.Count-1 do
for i := 0 to FLegendItems.Count - 1 do
if Checked[i] then begin
result := i;
Result := i;
exit;
end;
result := -1;
Result := -1;
end;
function TChartListbox.GetChecked(AIndex:integer) : boolean;
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;
ser: TBasicChartSeries;
begin
ser := GetSeries(AIndex);
result := ser.Active;
Result := ser.Active;
end;
function TChartListbox.GetLegendItem(AIndex:integer) : TLegendItem;
function TChartListbox.GetLegendItem(AIndex: Integer): TLegendItem;
begin
result := FLegendItems[AIndex];
Result := FLegendItems[AIndex];
end;
function TChartListbox.GetSeries(AIndex:integer) : TCustomChartSeries;
function TChartListbox.GetSeries(AIndex: Integer): TCustomChartSeries;
{ extracts, for the given index, the series from the internal
legend items list. }
var
legitem : TLegendItem;
legitem: TLegendItem;
begin
legitem := GetLegendItem(AIndex);
if (legitem <> nil) and (legitem.Owner is TCustomChartSeries) then
result := TCustomChartSeries(legitem.Owner)
Result := TCustomChartSeries(legitem.Owner)
else
result := nil;
Result := nil;
end;
function TChartListbox.GetSeriesCount : integer;
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;
Result := FLegendItems.Count;
end;
function TChartListbox.IsLocked : boolean;
function TChartListbox.IsLocked : Boolean;
begin
result := FLockCount <> 0;
Result := FLockCount <> 0;
end;
procedure TChartListBox.KeyDown(var Key: Word; Shift: TShiftState);
{ allows checking/unchecking of items by means of pressing the space bar }
begin
if (Key = VK_SPACE) and (Shift=[]) and FShowCheckboxes then begin
if (Key = VK_SPACE) and (Shift = []) and FShowCheckboxes then begin
ClickedCheckbox(ItemIndex);
Key := VK_UNKNOWN;
end else
end
else
inherited KeyDown(Key,Shift);
end;
@ -428,8 +437,8 @@ begin
AHeight := Max(AHeight, GetSystemMetrics(SM_CYMENUCHECK) + 2);
end;
procedure TChartListbox.MouseDown(AButton: TMouseButton; AShift: TShiftState;
AX, AY: Integer);
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
@ -440,22 +449,22 @@ procedure TChartListbox.MouseDown(AButton: TMouseButton; AShift: TShiftState;
checkbox nor the series icon.
}
var
Rcb, Ricon : TRect;
index : integer;
P : TPoint;
done : boolean;
rcb, ricon: TRect;
index: Integer;
p: TPoint;
done: Boolean;
begin
if (AButton = mbLeft) then begin
P := Point(AX, AY);
if AButton = mbLeft then begin
p := Point(AX, AY);
index := GetIndexAtXY(AX, AY);
CalcRects(ItemRect(index), Rcb, Ricon);
CalcRects(ItemRect(index), rcb, ricon);
FSeriesIconClicked := -1;
done := false;
if FShowCheckboxes and IsPointInRect(P, Rcb) then begin
if FShowCheckboxes and IsPointInRect(p, rcb) then begin
ClickedCheckbox(index);
done := true;
end;
if FShowSeriesIcons and PtInRect(Ricon, P) then begin
if FShowSeriesIcons and PtInRect(ricon, p) then begin
FSeriesIconClicked := index;
// remember the clicked index for the double click event
done := true;
@ -466,8 +475,8 @@ begin
inherited MouseDown(AButton, AShift, AX, AY);
end;
procedure TChartListbox.Notification(AComponent: TComponent;
AOperation: TOperation);
procedure TChartListbox.Notification(
AComponent: TComponent; AOperation: TOperation);
{ avoids AV if Chart is deleted from its container form }
begin
if (AOperation = opRemove) and (AComponent = FChart) then
@ -479,7 +488,7 @@ 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
i : integer;
i: Integer;
begin
try
Items.BeginUpdate;
@ -487,7 +496,7 @@ begin
if FChart <> nil then begin
FreeAndNil(FLegendItems);
FLegendItems := CreateLegendItems;
for i:=0 to FLegendItems.Count-1 do
for i := 0 to FLegendItems.Count - 1 do
Items.Add('');
// the caption is owner-drawn using info from the FLegendItems
// --> no need to pass the text here
@ -500,10 +509,10 @@ begin
end;
end;
procedure TChartListbox.RemoveSeries(ASeries:TCustomChartSeries);
procedure TChartListbox.RemoveSeries(ASeries: TCustomChartSeries);
{ removes the series from the listbox, but keeps it in the chart }
var
index : integer;
index: Integer;
begin
index := FindSeriesIndex(ASeries);
if index <> -1 then begin
@ -513,11 +522,11 @@ begin
end;
end;
procedure TChartListbox.SeriesChanged(ASender:TObject);
procedure TChartListbox.SeriesChanged(ASender: TObject);
{ Notification procedure of the listener. Responds to chart broadcasts
by populating the listbox with the chart's series }
var
index : integer;
index: Integer;
begin
if not IsLocked then begin
Populate;
@ -526,13 +535,13 @@ begin
if (FCheckStyle = cbsRadioButton) and (ASender is TBasicChartSeries) then
begin
index := FindSeriesIndex(ASender as TCustomChartSeries);
if (index <> -1) then
if index <> -1 then
Checked[index] := (ASender as TCustomChartSeries).Active;
end;
end;
end;
procedure TChartListbox.SetChart(AValue:TChart);
procedure TChartListbox.SetChart(AValue: TChart);
{ connects the ListBox to the chart }
begin
if FChart = AValue then exit;
@ -545,12 +554,12 @@ begin
SeriesChanged(nil);
end;
procedure TChartListbox.SetChecked(AIndex:integer; AValue:boolean);
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 : TBasicChartSeries;
i : integer;
ser: TBasicChartSeries;
i: Integer;
begin
ser := GetSeries(AIndex);
if ser <> nil then begin
@ -570,11 +579,11 @@ begin
end;
end;
procedure TChartlistbox.SetCheckStyle(AValue:TCheckBoxesStyle);
procedure TChartlistbox.SetCheckStyle(AValue: TCheckBoxesStyle);
{ selects "checkbox" or "radiobutton" styles. In radiobutton mode, only
one series can be visible }
var
j : integer;
j: Integer;
begin
if AValue <> FCheckStyle then begin
if AValue = cbsRadioButton then
@ -588,7 +597,7 @@ begin
end;
end;
procedure TChartListbox.SetShowCheckboxes(AValue:boolean);
procedure TChartListbox.SetShowCheckboxes(AValue: Boolean);
begin
if AValue <> FShowCheckboxes then begin
FShowCheckboxes := AValue;
@ -596,7 +605,7 @@ begin
end;
end;
procedure TChartListbox.SetShowSeriesIcons(AValue:boolean);
procedure TChartListbox.SetShowSeriesIcons(AValue: Boolean);
begin
if AValue <> FShowSeriesIcons then begin
FShowSeriesIcons := AValue;