{********************************************}
{ TeeChart Pro - TChartListBox class         }
{ Component Registration Unit                }
{                                            }
{ Copyright (c) 1995-2020 by Steema Software }
{        All Rights Reserved                 }
{********************************************}
unit TeeLisB;
{$I TeeDefs.inc}

interface

Uses {$IFNDEF LINUX}
     Windows,
     Messages,
     {$ENDIF}

     StdCtrls, Graphics, Forms, Controls, Buttons, Dialogs, ComCtrls,

     {$IFDEF D6}
     Types,
     {$ENDIF}

     SysUtils, Classes, Chart, TeeProcs, TeCanvas, TeEngine;

type
  // Note: These two types (TListBoxSection and TListBoxSections), should be here,
  // before any other declarations, to workaround a BCB bug. QC109128 / QC88686
  TListBoxSection=record
    Width   : Integer;
    Visible : Boolean;
  end;

  TListBoxSections=Array[0..3] of TListBoxSection;

  TChartListBox=class;

  TDblClickSeriesEvent=procedure(Sender:TChartListBox; Index:Integer) of object;
  TNotifySeriesEvent=procedure(Sender:TChartListBox; Series:TCustomChartSeries) of object;
  TChangeOrderEvent=procedure(Sender:TChartListBox; Series1,Series2:TCustomChartSeries) of object;

  {$IFDEF D16}
  [ComponentPlatformsAttribute(pidWin32 or pidWin64)]
  {$ENDIF}
  TChartListBox=class(TCustomListBox,ITeeEventListener)
  private
    FAllowAdd         : Boolean;
    FAllowDelete      : Boolean;
    FAskDelete        : Boolean;
    FChart            : TCustomChart;
    FCheckStyle       : TCheckBoxesStyle;
    FEditor           : TEdit;
    FEnableChangeColor: Boolean;
    FEnableDragSeries : Boolean;
    FEnableChangeType : Boolean;
    FFontSeriesColor  : Boolean;
    FGroup            : TSeriesGroup;

    {$IFNDEF LCL}
    FHitTest          : TPoint;
    {$ENDIF}

    FNames            : Boolean;

    FOnChangeActive   : TNotifySeriesEvent;
    FOnChangeColor    : TNotifySeriesEvent;
    FOnChangeOrder    : TChangeOrderEvent;

    FOnEditSeries     : TDblClickSeriesEvent;
    FOnRemovedSeries  : TNotifySeriesEvent;
    FOtherItems       : TStrings;
    FOtherItemsChange : TNotifyEvent;
    FRefresh          : TNotifyEvent;
    ComingFromDoubleClick:Boolean;

    IChanging         : Boolean;

    procedure CheckLegendBoxes;

    {$IFNDEF LCL}
    procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED;
    {$ENDIF}

    procedure DoRefresh;
    procedure EditorKey(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure EditorPress(Sender: TObject; var Key: Char);
    Function GetSelectedSeries:TChartSeries;
    Function GetSeriesGroup:TCustomSeriesList;
    Function GetShowActive:Boolean;
    Function GetShowIcon:Boolean;
    Function GetShowColor:Boolean;
    Function GetShowTitle:Boolean;
    function GetSeries(Index: Integer): TChartSeries;
    procedure LBSeriesClick(Sender: TObject);

    procedure LBSeriesDrawItem(Control: TWinControl; Index: Integer;
                               Rect: TRect; State: TOwnerDrawState);

    procedure LBSeriesDragOver( Sender, Source: TObject; X,
                                 Y: Integer; State: TDragState; var Accept: Boolean);
    Procedure RefreshDesigner;
    Function SectionLeft(ASection:Integer):Integer;
    Procedure SelectSeries(AIndex:Integer);
    procedure SetChart(const Value:TCustomChart);
    procedure SetCheckStyle(Value:TCheckBoxesStyle);
    procedure SetFontSeriesColor(const Value: Boolean);
    procedure SetGroup(const Value:TSeriesGroup);
    procedure SetNames(Value:Boolean);
    Procedure SetSelectedSeries(const Value:TChartSeries);
    Procedure SetShowActive(Value:Boolean);
    Procedure SetShowIcon(Value:Boolean);
    Procedure SetShowColor(Value:Boolean);
    Procedure SetShowTitle(Value:Boolean);
    function VerifyParent(out AControl:TWinControl):TWinControl;
  protected
    OtherItemsControl : TControl;
    
    procedure DblClick; override;

    {$IFNDEF FMX}
    {$IFNDEF LCL}
    procedure CreateWindowHandle(const Params: TCreateParams); override;
    {$ENDIF}
    {$ENDIF}

    {$IFDEF D6}
    function GetItemIndex: Integer; override;
    {$ENDIF}

    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
                        X, Y: Integer); override;
    procedure Notification(AComponent: TComponent;
                           Operation: TOperation); override;
    {$IFNDEF LCL}
    procedure WMNCHitTest(var Msg: TWMNCHitTest); message WM_NCHITTEST;
    procedure WMSetCursor(var Msg: TWMSetCursor); message WM_SETCURSOR;
    {$ENDIF}

    Procedure SetParent(Control:TWinControl); override;
    procedure TeeEvent(Event: TTeeEvent);  { interface }
  public
    Sections : TListBoxSections;

    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    Function AddSeriesGallery:TChartSeries;
    Function AnySelected:Boolean;
    procedure ChangeTypeSeries(Sender: TObject);
    procedure ClearItems;
    {$IFNDEF D6}
    procedure ClearSelection;
    {$ENDIF}
    Procedure CloneSeries;

    Function DeleteSeries:Boolean;
    procedure DragDrop(Source: TObject; X, Y: Integer); override;
    procedure FillSeries(const OldSeries:TChartSeries);

    class function GetBackColor(const AList:TControl; IsSelected,IsNotFocusedAndSelected,IsFocused:Boolean):TColor;
    class function GetFontColor(const AControl:TControl; const AFont:TFont; IsSelected:Boolean):TColor;

    procedure HideEditor;
    Procedure MoveCurrentDown;
    Procedure MoveCurrentUp;
    property OtherItems:TStrings read FOtherItems write FOtherItems;
    Function PointInSection(Const P:TPoint; ASection:Integer):Boolean;
    Function RenameSeries:Boolean;
    Procedure SelectAll; {$IFDEF D6}override;{$ENDIF}
    property Series[Index:Integer]:TChartSeries read GetSeries;
    property SeriesGroup:TSeriesGroup read FGroup write SetGroup;
    Function SeriesAtMousePos(out p:TPoint):Integer;
    Procedure ShowEditor;
    procedure SwapSeries(tmp1,tmp2:Integer);
    procedure UpdateSeries;

    property SelectedSeries:TChartSeries read GetSelectedSeries
                                         write SetSelectedSeries;
    property ShowSeriesNames:Boolean read FNames write SetNames;
  published
    property AllowAddSeries : Boolean read FAllowAdd
                                      write FAllowAdd default True;
    property AllowDeleteSeries : Boolean read FAllowDelete
                                         write FAllowDelete default True;
    property AskDelete:Boolean read FAskDelete write FAskDelete
                               default True;
    property Chart:TCustomChart read FChart write SetChart;

    property CheckStyle:TCheckBoxesStyle read FCheckStyle
                                         write SetCheckStyle default cbsCheck;

    property EnableChangeColor:Boolean read FEnableChangeColor
                                       write FEnableChangeColor default True;
    property EnableDragSeries:Boolean read FEnableDragSeries
                                      write FEnableDragSeries default True;
    property EnableChangeType:Boolean read FEnableChangeType
                                      write FEnableChangeType default True;
    property FontSeriesColor:Boolean read FFontSeriesColor
                                     write SetFontSeriesColor default False;

    property OnChangeActive:TNotifySeriesEvent read FOnChangeActive
                                               write FOnChangeActive;
    property OnChangeColor:TNotifySeriesEvent read FOnChangeColor
                                              write FOnChangeColor;
    property OnChangeOrder:TChangeOrderEvent read FOnChangeOrder
                                             write FOnChangeOrder;
    property OnDblClickSeries:TDblClickSeriesEvent read FOnEditSeries
                                                   write FOnEditSeries;
    property OnOtherItemsChange:TNotifyEvent read FOtherItemsChange
                                             write FOtherItemsChange;
    property OnRefresh:TNotifyEvent read FRefresh write FRefresh;
    property OnRemovedSeries:TNotifySeriesEvent read FOnRemovedSeries
                                                write FOnRemovedSeries;
    property ShowActiveCheck:Boolean read GetShowActive
                                     write SetShowActive default True;
    property ShowSeriesColor:Boolean read GetShowColor
                                     write SetShowColor default True;
    property ShowSeriesIcon:Boolean read GetShowIcon
                                    write SetShowIcon default True;
    property ShowSeriesTitle:Boolean read GetShowTitle
                                     write SetShowTitle default True;
    property OnDrawItem;
    property Align;
    property BorderStyle;
    property Color;
    {$IFNDEF LCL}
    property Ctl3D;
    {$ENDIF}
    property Enabled;
    property ExtendedSelect;
    property Font;
    {$IFNDEF LCL}
    property ImeMode;
    property ImeName;
    {$ENDIF}
    property ItemHeight;
    property MultiSelect default True;
    property ParentColor;
    {$IFNDEF LCL}
    property ParentCtl3D;
    {$ENDIF}
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShowHint;
    property Sorted;
    property TabOrder;
    property TabStop;
    property Visible;
    property OnClick;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;

    {$IFDEF D10}
    property OnMouseEnter;
    property OnMouseLeave;
    {$ENDIF}

    property OnMouseMove;
    property OnMouseUp;
    property OnStartDock;
    property OnStartDrag;
  end;

var
  TeeAddGalleryProc:Function(const AOwner:TComponent; const Chart:TCustomChart; const Series:TChartSeries):TChartSeries=nil;
  TeeChangeGalleryProc:Function(const AOwner:TComponent; var Series: TChartSeries):TChartSeriesClass=nil;

implementation

{$R TeeBmps.res}

Uses {$IFDEF LCL}
     LCLType, // Pending to move "odSelected" to StdCtrls unit.
     {$ENDIF}
     {$IFDEF D16}
     Vcl.Themes,
     {$ENDIF}
     TeePenDlg, TeeConst;

{ TChartListBox }
Constructor TChartListBox.Create(AOwner:TComponent);
begin
  inherited;

  ComingFromDoubleClick:=False;

//  DoubleBuffered:=True;

  FEnableChangeColor:=True;
  FEnableDragSeries:=True;
  FEnableChangeType:=True;

  Sections[0].Width:=26;  Sections[0].Visible:=True;
  Sections[1].Width:=16;  Sections[1].Visible:=True;
  Sections[2].Width:=26;  Sections[2].Visible:=True;
  Sections[3].Width:=216; Sections[3].Visible:=True;

  OnDrawItem:=LBSeriesDrawItem;
  OnDragOver:=LBSeriesDragOver;
  OnClick:=LBSeriesClick;

  {$IFDEF TEEOCX} // MM lbOwnerDrawFixed breaks AX ListBox in .NET form
  Style:=lbOwnerDrawVariable;
  {$ELSE}
  Style:=lbOwnerDrawFixed;
  {$ENDIF}

  ItemHeight:=24;
  Sorted:=False;
  MultiSelect:=True;
  FAskDelete:=True;
  FAllowDelete:=True;
  FAllowAdd:=True;
end;

Destructor TChartListBox.Destroy;
begin
  FreeAndNil(FEditor);
  Chart:=nil;

  inherited;
end;

procedure TChartListBox.DragDrop(Source: TObject; X,Y: Integer);
var tmp1 : Integer;
    tmp2 : Integer;
begin
  if ItemIndex<>-1 then
  begin
    tmp1:=ItemIndex;
    tmp2:=ItemAtPos(TeePoint(X,Y),True);
    if (tmp2<>-1) and (tmp1<>tmp2) then SwapSeries(tmp1,tmp2);
  end;
end;

{$IFNDEF LCL}
// This (empty) method is necessary to force repaint after changing the listbox
// Color property. (VCL bug?)
procedure TChartListBox.CMColorChanged(var Message: TMessage);
begin
  inherited;
end;
{$ENDIF}

procedure TChartListBox.DoRefresh;
begin
  if Assigned(FRefresh) then FRefresh(Self);
end;

type
  TChartAccess=class(TCustomChart);

procedure TChartListBox.SetChart(const Value:TCustomChart);
begin
  if FChart<>Value then
  begin
    if Assigned(Chart) then
    begin
      TChartAccess(Chart).RemoveListener(Self);
      Chart.RemoveFreeNotification(Self);
    end;

    FChart:=Value;

    if Assigned(Chart) then
    begin
      Chart.FreeNotification(Self);
      TChartAccess(Chart).AddListener(Self);
      FillSeries(nil);
    end
    else
      ClearItems;
  end;
end;

procedure TChartListBox.SetCheckStyle(Value:TCheckBoxesStyle);
var tmpCount : Integer;
    t        : Integer;
    tmpNum   : Integer;
begin
  if FCheckStyle<>Value then
  begin
    FCheckStyle:=Value;

    if FCheckStyle=cbsRadio then
    begin
      tmpCount:=0;

      // Avoid VCL bug (Handle cannot be obtained)
      if Assigned(Parent) and Parent.Showing then
      begin
        tmpNum:=Items.Count-1;

        // Ensure only one series is active (visible)
        for t:=0 to tmpNum do
        if Series[t].Active then
        begin
          Inc(tmpCount);
          if tmpCount>1 then Series[t].Active:=False;
        end;
      end;
    end;

    Invalidate;
  end;
end;

procedure TChartListBox.ClearItems;
begin
  if not (csDestroying in ComponentState) then
  begin
    Items.Clear;

    if Assigned(FOtherItems) then
    begin
      // Workaround a VCL bug when FOtherItems is a ComboBox.Items,
      // and the ComboBox.Parent(s) arent "Showing"

      if Assigned(OtherItemsControl) and (GetParentForm(OtherItemsControl)<>nil) then
      try
        FOtherItems.Clear;
      except
        on E:EInvalidOperation do ;  // "eat" this exception
      end;
    end;
  end;
end;

Procedure TChartListBox.SelectSeries(AIndex:Integer);
begin
  if MultiSelect then Selected[AIndex]:=True
                 else ItemIndex:=AIndex;
end;

procedure TChartListBox.SwapSeries(tmp1,tmp2:Integer);
var tmp        : TCustomForm;
    Series1    : TCustomChartSeries;
    Series2    : TCustomChartSeries;
begin
  Items.Exchange(tmp1,tmp2);

  if Assigned(FOtherItems) then
     FOtherItems.Exchange(tmp1,tmp2);

  if Assigned(Chart) then
  begin
    Series1:=Series[tmp1];
    Series2:=Series[tmp2];
    Chart.ExchangeSeries(Series1,Series2);

    if Assigned(FOnChangeOrder) then
       FOnChangeOrder(Self,Series1,Series2);
  end;

  tmp:=GetParentForm(Self);

  if Assigned(tmp) and CanFocus then
     tmp.ActiveControl:=Self;

  SelectSeries(tmp2);
  DoRefresh;
  RefreshDesigner;
end;

procedure TChartListBox.LBSeriesClick(Sender: TObject);
begin
  DoRefresh;
end;

Function TChartListBox.SectionLeft(ASection:Integer):Integer;
var t : Integer;
begin
  result:=0;

  for t:=0 to ASection-1 do
      if Sections[t].Visible then
         Inc(result,Sections[t].Width);
end;

procedure TChartListBox.SetGroup(const Value:TSeriesGroup);
begin
  if FGroup<>Value then
  begin
    FGroup:=Value;
    UpdateSeries;
  end;
end;

type
  TSeriesAccess=class(TChartSeries);

procedure TChartListBox.LBSeriesDrawItem(Control: TWinControl;
                    Index: Integer; Rect: TRect; State: TOwnerDrawState);

  function ChartShadow(const AChart:TCustomAxisPanel):TTeeShadow;
  begin
    if Assigned(AChart) then
    begin
      result:=TCustomChart(AChart).Legend.Symbol.Shadow;

      if not result.Visible then
         result:=nil
    end
    else
      result:=nil;
  end;

  function ItemIsSelected(Index:Integer):Boolean;
  begin
    result:=(Index=ItemIndex) or
            (MultiSelect and Selected[Index]);
  end;

var tmp       : Integer;
    tmpSeries : TChartSeries;
    tmpR      : TRect;
    tmpCanvas : TCanvas;
    tmpSelected : Boolean;
    OldShadowX : Integer;
    OldShadowY : Integer;
    tmpShadow  : TTeeShadow;
    tmpHasParent : Boolean;
    OldAutoRepaint : Boolean;
begin
  tmpCanvas:=Canvas;

  tmpSelected:=(odSelected in State) or ((not Focused) and ItemIsSelected(Index));

  With tmpCanvas do
  begin
    Brush.Color:=GetBackColor(Self,tmpSelected,(not Focused) and ItemIsSelected(Index),odFocused in State);

    FillRect(Rect);

    {$IFNDEF D16}
    Brush.Color:=Self.Color;
    Brush.Style:=bsSolid;
    tmpR:=Rect;
    tmpR.Right:=SectionLeft(3)-2;

    FillRect(tmpR);
    {$ENDIF}

    tmpSeries:=Series[Index];

    if Assigned(tmpSeries) then
    begin
      if ShowSeriesIcon then
         TeeDrawBitmapEditor(tmpCanvas,tmpSeries,SectionLeft(0),Rect.Top);

      if ShowSeriesColor and
         (TSeriesAccess(tmpSeries).IUseSeriesColor) then
      begin
        tmp:=SectionLeft(2)-2;
        tmpR:=TeeRect(tmp,Rect.Top,tmp+Sections[2].Width,Rect.Bottom);
        InflateRect(tmpR,-4,-4);

        tmpHasParent:=Assigned(tmpSeries.ParentChart);

        OldAutoRepaint:=tmpHasParent and tmpSeries.ParentChart.AutoRepaint;

        if tmpHasParent then
           tmpShadow:=ChartShadow(tmpSeries.ParentChart)
        else
           tmpShadow:=nil;

        if Assigned(tmpShadow) then
        with tmpShadow do
        begin
          OldShadowX:=HorizSize;
          OldShadowY:=VertSize;

          tmpSeries.ParentChart.AutoRepaint:=False;

          Size:=3;
        end
        else
        begin
          OldShadowX:=0;
          OldShadowY:=0;
        end;

        PaintSeriesLegend(tmpSeries,tmpCanvas,tmpR);

        if Assigned(tmpShadow) then
        with tmpShadow do
        begin
          HorizSize:=OldShadowX;
          VertSize:=OldShadowY;

          tmpSeries.ParentChart.AutoRepaint:=OldAutoRepaint;
        end;
      end;

      if ShowActiveCheck then
      begin
        tmp:=SectionLeft(1);
        TeeDrawCheckBox(tmp+2,Rect.Top+((Rect.Bottom-Rect.Top-13) div 2),
                        tmpCanvas,tmpSeries.Active,Self.Color,CheckStyle=cbsCheck);
      end;

      Brush.Style:=bsClear;

      if ShowSeriesTitle then
      begin
        if tmpSelected and Focused then
           Font.Color:=GetFontColor(Self,Self.Font,True)
        else
        if FFontSeriesColor then
           Font.Color:=tmpSeries.Color
        else
           Font.Color:=GetFontColor(Self,Self.Font,False);

        {$IFDEF MSWINDOWS}
        SetBkMode(Handle,Transparent);
        {$ENDIF}

        TextOut(SectionLeft(3)+1,Rect.Top+((ItemHeight-TextHeight(TeeCharForHeight)) div 2),Items[Index]);

        {$IFDEF MSWINDOWS}
        SetBkMode(Handle,Opaque);
        {$ENDIF}
      end
      else TextOut(0,0,'');

    end;
  end;
end;

Function TChartListBox.GetSelectedSeries:TChartSeries;
begin
  result:=Series[ItemIndex];
end;

procedure TChartListBox.SetFontSeriesColor(const Value: Boolean);
begin
  if FFontSeriesColor<>Value then
  begin
    FFontSeriesColor:=Value;
    Invalidate;
  end;
end;

procedure TChartListBox.SetNames(Value:Boolean);
begin
  if FNames<>Value then
  begin
    FNames:=Value;
    UpdateSeries;
  end;
end;

Procedure TChartListBox.SetSelectedSeries(const Value:TChartSeries);
begin
  ItemIndex:=Items.IndexOfObject(Value);

  if ItemIndex<>-1 then
     Selected[ItemIndex]:=True;
end;

procedure TChartListBox.LBSeriesDragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
begin
  Accept:=FEnableDragSeries and (Sender=Source);
end;

Function TChartListBox.SeriesAtMousePos(out p:TPoint):Integer;
begin
  p:=ScreenToClient(Mouse.CursorPos);
  result:=ItemAtPos(p,True);
end;

Function TChartListBox.PointInSection(Const P:TPoint; ASection:Integer):Boolean;
var tmpPos : Integer;
    tmpRight : Integer;
begin
  if Sections[ASection].Visible then
  begin
    tmpPos:=SectionLeft(ASection);

    if ASection=High(Sections) then
       tmpRight:=Width
    else
       tmpRight:=tmpPos+Sections[ASection].Width;

    result:=(p.x>tmpPos) and (p.x<tmpRight);
  end
  else
    result:=False;
end;

Function TChartListBox.GetSeriesGroup:TCustomSeriesList;
begin
  if Assigned(FGroup) then result:=FGroup.Series
                      else result:=Chart.SeriesList;
end;

procedure TChartListBox.CheckLegendBoxes;
begin
  if Chart.Legend.CheckBoxes then
     CheckStyle:=Chart.Legend.CheckBoxesStyle
  else
     CheckStyle:=cbsCheck;
end;

procedure TChartListBox.FillSeries(const OldSeries:TChartSeries);
var tmp : Integer;
    OldParent : TWinControl;
    tmpPanel  : TWinControl;
    tmpList   : TCustomSeriesList;
Begin
  if not HandleAllocated then
     Exit;

  tmpPanel:=VerifyParent(OldParent);

  ClearItems;

  if Assigned(Chart) then
  begin
    tmpList:=GetSeriesGroup;

    if Assigned(tmpList) then
       FillSeriesItems(Items,tmpList,not FNames);

    CheckLegendBoxes;
  end;

  if Assigned(FOtherItems) then
  begin
    // Workaround a VCL bug, when FOtherItems is a ComboBox.Items,
    // and the ComboBox.Parent(s) aren't "Showing"

    if Assigned(OtherItemsControl) and (GetParentForm(OtherItemsControl)<>nil) then
    try
       FOtherItems.Assign(Items);
    except
       on E:EInvalidOperation do ; // "eat" this exception
    end;
  end;

  tmp:=Items.IndexOfObject(OldSeries);
  if (tmp=-1) and (Items.Count>0) then tmp:=0;

  if (tmp<>-1) and (Items.Count>tmp) then SelectSeries(tmp);

  if Assigned(FOtherItemsChange) then
     FOtherItemsChange(Self);

  DoRefresh;

  Parent:=OldParent;
  tmpPanel.Free;
end;

Function TChartListBox.AnySelected:Boolean;
begin
  if MultiSelect then
  begin
    result:=SelCount>0;

    // Workaround VCL bug:
    if (not result) and (Items.Count>0) and (ItemIndex<>-1) then
    begin
      Selected[ItemIndex]:=True;
      result:=True;
    end;
  end
  else
    result:=ItemIndex<>-1;
end;

procedure TChartListBox.ChangeTypeSeries(Sender: TObject);
var tmpSeries : TChartSeries;
    NewClass  : TChartSeriesClass;
    t         : Integer;
    tmp       : Integer;
    FirstTime : Boolean;
    tmpList   : TChartSeriesList;
begin
  if AnySelected and Assigned(Chart) and Assigned(TeeChangeGalleryProc) then
  begin
    tmpList:=TChartSeriesList.Create;
    try
      { get selected list of series }
      if MultiSelect then
      begin
        for t:=0 to Items.Count-1 do
            if Selected[t] then tmpList.Add(Series[t])
      end
      else
        tmpList.Add(Series[ItemIndex]);

      FirstTime:=True;
      NewClass:=nil;

      IChanging:=True;
      try
        { change all selected... }
        for t:=0 to tmpList.Count-1 do
        begin
          tmpSeries:=tmpList[t];

          if FirstTime then
          begin
            NewClass:=TeeChangeGalleryProc(Owner,tmpSeries);
            FirstTime:=False;
          end
          else
            ChangeSeriesType(tmpSeries,NewClass);

          tmpList[t]:=tmpSeries;
        end;

        UpdateSeries;
      finally
        IChanging:=False;
      end;

      { reset the selected list... }
      ClearSelection;

      for t:=0 to tmpList.Count-1 do
      begin
        tmp:=Items.IndexOfObject(tmpList[t]);

        if tmp<>-1 then
           SelectSeries(tmp);
      end;
    finally
      tmpList.Free;
    end;

    { refill and repaint list }
    DoRefresh;

    { tell the series designer we have changed... }
    RefreshDesigner;
  end;
end;

procedure TChartListBox.DblClick;

  Function IsSelected(tmp:Integer):Boolean;
  begin
    if MultiSelect then result:=Selected[tmp]
                   else result:=ItemIndex=tmp;
  end;

var p        : TPoint;
    tmp      : Integer;
    tmpColor : TColor;
begin
  ComingFromDoubleClick:=True;

  // Find series at mouse position "p"
  tmp:=SeriesAtMousePos(p);

  if (tmp<>-1) and IsSelected(tmp) then
  begin
    if PointInSection(p,0) and FEnableChangeType then
       ChangeTypeSeries(Self)
    else
    if PointInSection(p,2) and FEnableChangeColor then
    begin
      if TSeriesAccess(Series[tmp]).IUseSeriesColor then
      With Series[tmp] do
      begin
        tmpColor:=SeriesColor;

        if EditColorDialog(Self,tmpColor) then
        begin
          SeriesColor:=tmpColor;
          ColorEachPoint:=False;

          if Assigned(FOnChangeColor) then
             FOnChangeColor(Self,Series[tmp]);
        end;
      end;
    end
    else
    if PointInSection(p,3) then
       if Assigned(FOnEditSeries) then
          FOnEditSeries(Self,tmp);
  end;
end;

{$IFDEF D6}
function TChartListBox.GetItemIndex: Integer;
begin
  result:=inherited GetItemIndex;

  if Count=0 then
     result:=-1;
end;
{$ENDIF}

procedure TChartListBox.MouseDown(Button: TMouseButton; Shift: TShiftState;
                                  X, Y: Integer);
var tmp : Integer;
    t   : Integer;
    p   : TPoint;
begin
  inherited;

  HideEditor;

  if ItemIndex<>-1 then
  begin
    tmp:=SeriesAtMousePos(p);

    if (tmp<>-1) and (PointInSection(p,1)) then
    begin

      if CheckStyle=cbsCheck then
         with Series[tmp] do
              Active:=not Active
      else
         for t:=0 to Items.Count-1 do
             Series[t].Active:=t=tmp;

      if not Assigned(Series[tmp].ParentChart) then
         Repaint;

      if Assigned(FOnChangeActive) then
         FOnChangeActive(Self,Series[tmp]);
    end
    else
    if FEnableDragSeries and
       PointInSection(p,3) and (not ComingFromDoubleClick) and
       (not (ssShift in Shift)) and
       (not (ssCtrl in Shift)) and
       ( ( ssLeft in Shift) ) then
            BeginDrag(False);

    ComingFromDoubleClick:=False;

    if Assigned(FOtherItemsChange) then
       FOtherItemsChange(Self);
  end;
end;

Procedure TChartListBox.MoveCurrentUp;
begin
  if ItemIndex>0 then
     SwapSeries(ItemIndex,ItemIndex-1);
end;

Procedure TChartListBox.MoveCurrentDown;
begin
  if (ItemIndex<>-1) and (ItemIndex<Items.Count-1) then
     SwapSeries(ItemIndex,ItemIndex+1);
end;

{ Delete the current selected Series, if any. Returns True if deleted }
Function TChartListBox.DeleteSeries:Boolean;

  Procedure DoDelete;
  Var t : Integer;
  begin
    if MultiSelect then
    begin
      t:=0;
      While t<Items.Count do
      if Selected[t] then Series[t].Free
                     else Inc(t);
    end
    else
    if ItemIndex<>-1 then Series[ItemIndex].Free;

    if Items.Count>0 then SelectSeries(0);

    DoRefresh;
    RefreshDesigner;
  end;

var tmpSt : String;
begin
  result:=False;

  if AnySelected then
    if FAskDelete then
    begin
      if (not MultiSelect) or (SelCount=1) then
         tmpSt:=SeriesTitleOrName(SelectedSeries,True)
      else
         tmpSt:=TeeMsg_SelectedSeries;

      if TeeYesNoDelete(tmpSt,Self) then
      begin
        DoDelete;
        result:=True;
      end;
    end
    else
    begin
      DoDelete;
      result:=True;
    end;
end;

Procedure TChartListBox.CloneSeries;
begin { duplicate current selected series }
  if ItemIndex<>-1 then
  begin
    CloneChartSeries(SelectedSeries);
    RefreshDesigner;
  end;
end;

procedure TChartListBox.KeyUp(var Key: Word; Shift: TShiftState);
begin
  Case Key of
    TeeKey_Insert : if FAllowAdd then AddSeriesGallery;
    TeeKey_Delete : if FAllowDelete then DeleteSeries;
    TeeKey_F2     : if Assigned(SelectedSeries) then ShowEditor;
  end;
end;

procedure TChartListBox.EditorPress(Sender: TObject; var Key: Char);
begin
  if (Key=#13) or (Key=#27) then Key:=#0; // avoid "beep"
end;

procedure TChartListBox.EditorKey(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  case Key of
    TeeKey_Escape,
    TeeKey_F2,
    TeeKey_Up,
    TeeKey_Down,
    TeeKey_Return: if Assigned(FEditor) and FEditor.Visible then
                   begin
                     if Key<>TeeKey_Escape then
                        TChartSeries(FEditor.Tag).Title:=FEditor.Text;
                     FEditor.Hide;
                     SetFocus;
                     Invalidate;
                   end;
  end;
end;

type
  TControlAccess=class(TControl);

class function TChartListBox.GetBackColor(const AList:TControl; IsSelected,IsNotFocusedAndSelected,IsFocused:Boolean):TColor;

  function GetStyleColor(const AColor:TColor):TColor;
  begin
    {$IFDEF D16}
    if TStyleManager.IsCustomStyleActive then
       result:=StyleServices.GetSystemColor(AColor)
    else
    {$ENDIF}
      result:=AColor;
  end;

begin
  if IsSelected then
    if IsNotFocusedAndSelected then
       result:=GetStyleColor(clInactiveCaption)
    else
       result:=GetStyleColor(clHighLight)
  else
    if IsFocused then
       result:=GetStyleColor(clInactiveCaption)
    else
       result:=TControlAccess(AList).Color;
end;

class function TChartListBox.GetFontColor(const AControl:TControl; const AFont:TFont; IsSelected:Boolean):TColor;
begin
  {$IFDEF D16}
  if TStyleManager.IsCustomStyleActive
     {$IFDEF D17}
     and (seClient in AControl.StyleElements) and (seFont in AControl.StyleElements)
     {$ENDIF}
     then
     if IsSelected then
        result:=StyleServices.GetStyleFontColor(sfListItemTextSelected)
     else
        result:=StyleServices.GetStyleFontColor(sfListItemTextNormal)
  else
  {$ENDIF}
  if IsSelected then
     result:=ColorToRGB(clHighlightText)
  else
     result:=TTeeCanvas.ColorFrom(AFont.Color,0);
end;

procedure TChartListBox.HideEditor;
var c : Word;
begin
  c:=TeeKey_Return;
  EditorKey(FEditor,c,[]);
end;

Procedure TChartListBox.ShowEditor;
var R : TRect;
begin
  if Assigned(SelectedSeries) and ShowSeriesTitle then
  begin
    if not Assigned(FEditor) then
       FEditor:=TEdit.Create(Self);

    with FEditor do
    begin
      Parent:=Self;
      Tag:=ObjectToTag(SelectedSeries);
      Text:=SeriesTitleOrName(SelectedSeries,True);
      Width:=Sections[3].Width;
      Left:=SectionLeft(3)-2;
      R:=ItemRect(ItemIndex);
      Top:=R.Top;
      Height:=R.Bottom-R.Top+1;
      OnKeyDown:=EditorKey;
      OnKeyPress:=EditorPress;

      Show;

      {$IFDEF TEEOCX}
      Windows.SetFocus(Handle);
      {$ELSE}
      SetFocus;
      {$ENDIF}
    end;
  end;
end;

Function TChartListBox.RenameSeries:Boolean;
var tmp       : Integer;
    tmpSeries : TChartSeries;
    tmpSt     : String;
    Old       : String;
begin
  result:=False;
  tmp:=ItemIndex;

  if tmp<>-1 then
  begin
    tmpSeries:=SelectedSeries;

    if FNames then tmpSt:=tmpSeries.Name
              else tmpSt:=SeriesTitleOrName(tmpSeries,True);

    Old:=tmpSt;

    if InputQuery(TeeMsg_ChangeSeriesTitle,
                  TeeMsg_NewSeriesTitle,tmpSt) then
    begin
      if FNames then
      begin
        if Trim(tmpSt)<>'' then
        begin
          tmpSeries.Name:=tmpSt;
          Items[tmp]:=tmpSt;
          tmpSeries.Repaint;
        end;
      end
      else
      if (Trim(tmpSt)='') or (Trim(tmpSt)=tmpSeries.Name) then
         tmpSeries.Title:=''
      else
         tmpSeries.Title:=tmpSt;

      // return True if the Series title has been changed

      if FNames then result:=Old<>tmpSeries.Name
                else result:=Old<>SeriesTitleOrName(tmpSeries,True);
    end;

    SelectSeries(tmp);
  end;
end;

Procedure TChartListBox.RefreshDesigner;
begin
  if Assigned(Chart) and Assigned(Chart.Designer) then Chart.Designer.Refresh;
end;

Function TChartListBox.AddSeriesGallery:TChartSeries;
var t : Integer;
begin
  if Assigned(TeeAddGalleryProc) then
  begin
    result:=TeeAddGalleryProc(Owner,Chart,SelectedSeries);

    if Assigned(result) then
    begin
      if MultiSelect then
         for t:=0 to Items.Count-1 do Selected[t]:=False;

      SelectSeries(Items.IndexOfObject(result));

      DoRefresh;
      RefreshDesigner;
    end;
  end
  else result:=nil;
end;

procedure TChartListBox.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if (Operation=opRemove) and Assigned(Chart) and (AComponent=Chart) then
     Chart:=nil;
end;

{$IFNDEF LCL}
procedure TChartListBox.WMNCHitTest(var Msg: TWMNCHitTest);
begin
  inherited;
  FHitTest:=SmallPointToPoint(Msg.Pos);
end;

procedure TChartListBox.WMSetCursor(var Msg: TWMSetCursor);
var I: Integer;
begin
  if csDesigning in ComponentState then Exit;

  if (Items.Count>0) and (SeriesAtMousePos(FHitTest)<>-1) and
     (Msg.HitTest=HTCLIENT) then

     for I:=0 to 2 do  { don't count last section }
     begin
       if PointInSection(FHitTest,I) then
       begin
         if ((I=0) and FEnableChangeType) or
            ((I=2) and FEnableChangeColor) then
         begin
           SetCursor(Screen.Cursors[crHandPoint]);
           Exit;
         end
         else break;
       end;
     end;

  inherited;
end;
{$ENDIF}

{$IFNDEF FMX}
{$IFNDEF LCL}
procedure TChartListBox.CreateWindowHandle(const Params: TCreateParams);
begin
  inherited;
  UpdateSeries;
end;
{$ENDIF}
{$ENDIF}

procedure TChartListBox.UpdateSeries;
begin
  // Workaround to VCL limitation to avoid exception

  if HandleAllocated then
     FillSeries(SelectedSeries);
end;

Function TChartListBox.GetShowActive:Boolean;
begin
  result:=Sections[1].Visible;
end;

procedure TChartListBox.SetShowActive(Value:Boolean);
begin
  Sections[1].Visible:=Value; Repaint;
end;

Function TChartListBox.GetShowIcon:Boolean;
begin
  result:=Sections[0].Visible;
end;

procedure TChartListBox.SetShowIcon(Value:Boolean);
begin
  Sections[0].Visible:=Value; Repaint;
end;

Function TChartListBox.GetShowColor:Boolean;
begin
  result:=Sections[2].Visible;
end;

procedure TChartListBox.SetShowColor(Value:Boolean);
begin
  Sections[2].Visible:=Value; Repaint;
end;

Function TChartListBox.GetShowTitle:Boolean;
begin
  result:=Sections[3].Visible;
end;

procedure TChartListBox.SetShowTitle(Value:Boolean);
begin
  Sections[3].Visible:=Value; Repaint;
end;

function TChartListBox.VerifyParent(out AControl:TWinControl):TWinControl;
begin
  AControl:=Parent;
  result:=nil;

  // This exception happens when a TabSheet has no parent.
  // Workaround is to use a temporary Form as Parent.
  try
    HandleNeeded;
  except
    on EInvalidOperation:Exception do
    begin
      try
        result:=TForm.Create(nil);
        Parent:=result;
        HandleNeeded;
      except
        on E:Exception do
           Exit;
      end;
    end;
  end;
end;

procedure TChartListBox.SelectAll;
{$IFNDEF D6}
var t : Integer;
{$ENDIF}
begin
  if MultiSelect then
  begin
    {$IFDEF D6}
    inherited;
    {$ELSE}
    for t:=0 to Items.Count-1 do
        Selected[t]:=True;
    {$ENDIF}

    RefreshDesigner;
  end;
end;

Function TChartListBox.GetSeries(Index:Integer):TChartSeries;
begin
  if (Index<>-1) and (Index<Items.Count) then
     result:=TChartSeries(Items.Objects[Index])
  else
     result:=nil;
end;

procedure TChartListBox.TeeEvent(Event:TTeeEvent);

  procedure SeriesRemoved;
  var OldParent : TWinControl;
      tmpPanel  : TWinControl;
      tmp       : Integer;
  begin
    tmpPanel:=VerifyParent(OldParent);

    tmp:=Items.IndexOfObject(TTeeSeriesEvent(Event).Series);

    if tmp<>-1 then
    begin
      Items.Delete(tmp);

      if Assigned(FOtherItems) then
         FOtherItems.Delete(tmp);

      if Assigned(FOnRemovedSeries) then
         FOnRemovedSeries(Self,TTeeSeriesEvent(Event).Series);
    end;

    Parent:=OldParent;
    tmpPanel.Free;
  end;

var tmp : Integer;
begin
  if not (csDestroying in ComponentState) then
  if Event is TTeeSeriesEvent then
  With TTeeSeriesEvent(Event) do
  Case Event of
    seChangeColor : if not IChanging then
                       Invalidate;

   seChangeActive : begin
                      CheckLegendBoxes;
                      Invalidate;
                    end;

    seChangeTitle : begin
                     tmp:=Items.IndexOfObject(Series);
                     if tmp<>-1 then
                        Items[tmp]:=SeriesTitleOrName(Series,True);
                    end;

         seRemove : SeriesRemoved;

           seAdd  : if not IChanging then
                       UpdateSeries;

           seSwap : if not IChanging then
                       UpdateSeries;
  end
  else
  if Event is TTeeView3DEvent then
     Invalidate // TV52010062
  else
  if Event is TTeeLegendEvent then
     CheckLegendBoxes;
end;

{$IFNDEF D6}
Procedure TChartListBox.ClearSelection;
var t: Integer;
begin
  if MultiSelect then
     for t:=0 to Items.Count-1 do
         Selected[t]:=False
  else
     ItemIndex:=-1;
end;
{$ENDIF}

Procedure TChartListBox.SetParent(Control:TWinControl);
begin
  inherited;

  // VCL Bug / Limitation:
  // If "not Parent.Showing" then Items.Count raises a
  // "Control has no Parent Window" exception
  if Assigned(Parent) and Parent.Showing then
     if (ItemIndex<>-1) and (Items.Count=0) then
         ItemIndex:=-1;
end;

end.

