{**********************************************}
{   TOrgSeries Organizational Charts           }
{   Copyright (c) 2011-2020 by Steema Software }
{**********************************************}
unit TeeOrgSeries;
{$I TeeDefs.inc}

interface

uses
  {$IFNDEF LINUX}
  {$IFNDEF FMX}
  Windows,
  {$ENDIF}
  {$ENDIF}
  Classes, SysUtils, Math,

  {$IFDEF D18}
  System.Generics.Collections,
  {$ENDIF}
  
  {$IFDEF FMX}
  System.UITypes, System.Types, FMX.Types,
  {$ELSE}

  {$IFDEF D16}
  System.UITypes, System.Types,
  {$ENDIF}

  Controls, Graphics,
  {$ENDIF}

  {$IFDEF FMX}
  FMXTee.Canvas, FMXTee.Engine, FMXTee.Chart, FMXTee.Tools, FMXTee.Constants,
  FMXTee.Procs
  {$ELSE}
  TeCanvas, TeEngine, Chart, TeeTools, TeeConst, TeeProcs
  {$ENDIF};

type
  TOrgShape=class(TTextShape)
  private
    IOwner : TChartSeries;
  public
    procedure Repaint; override;
  published
    property CustomPosition default True;
    property Left stored False;
    property Top stored False;
    property Text stored False;
    property TextAlignment default taCenter;
  end;

  TCustomOrgSeries=class;  // forward

  TOrgItem=class(TCollectionItem)
  private
    function GetFormat:TOrgShape;
    function GetHeight: Integer;
    function GetSuperior: Integer;
    function GetText: String;
    function GetWidth: Integer;
    procedure SetFormat(const Value: TOrgShape);
    procedure SetHeight(const Value: Integer);
    procedure SetSuperior(const Value: Integer);
    procedure SetText(const Value: String);
    procedure SetWidth(const Value: Integer);
  protected
    FFormat : TOrgShape;

    NumTextLines : Integer;

    function ActiveFormat:TOrgShape;
    function HasFormat:Boolean;
  public
    ShapeBounds : TRect;

    Destructor Destroy; override;

    function AddChild(const Text:String):TOrgItem;
    function AddBrother(const Text:String):TOrgItem;
    procedure Assign(Source:TPersistent); override;

    function Level:Integer;
    function Parent:TOrgItem;
    procedure RemoveFormat;
    function Series:TCustomOrgSeries;
  published
    property Format:TOrgShape read GetFormat write SetFormat stored HasFormat;
    property Height:Integer read GetHeight write SetHeight stored False;
    property Superior:Integer read GetSuperior write SetSuperior stored False;
    property Text:String read GetText write SetText stored False;
    property Width:Integer read GetWidth write SetWidth stored False;
  end;

  TOrgItemCollection=class(TOwnedCollection)
  private
    Function Get(Index:Integer):TOrgItem;
    Procedure Put(Index:Integer; Const Value:TOrgItem);
  public
    property Items[Index:Integer]:TOrgItem read Get write Put; default;
  end;

  TSpacing=class(TPersistent)
  private
    FVert: Integer;
    FHoriz: Integer;

    ISeries : TChartSeries;

    procedure SetHoriz(const Value: Integer);
    procedure SetVert(const Value: Integer);
  public
    Constructor Create;
    procedure Assign(Source:TPersistent); override;
  published
    property Horizontal:Integer read FHoriz write SetHoriz default 8;
    property Vertical:Integer read FVert write SetVert default 16;
  end;

  TOrgLineStyle=(lsSquared,lsDiagonal);

  TCustomOrgSeries=class(TChartSeries)
  private
    FDefault   : TOrgShape;
    FNodes     : TOrgItemCollection;
    FSpacing   : TSpacing;

    function GetItem(Index: Integer): TOrgItem;
    procedure SetDefault(const Value: TOrgShape);
    procedure SetItem(Index: Integer; const Value: TOrgItem);
    procedure SetNodes(const Value: TOrgItemCollection);
    procedure SetSpacing(const Value: TSpacing);
  protected
    IBounds    : TRect;

    procedure AssignFormatting(Source:TChartSeries); override;
    procedure AutoSizeNodes;
    Function CheckMouse(x,y:Integer):Boolean; override;
    procedure ClearLists; override;
    function GetChilds(Superior:Integer; VisibleOnly:Boolean):TIntArray; virtual;
    class Function GetEditorClass:String; override;
    procedure Loaded; override;
    procedure SetParentChart(const Value: TCustomAxisPanel); override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    function Add(const Text:String; Superior:Integer=-1):Integer; overload;
    Function AddXY(Const AXValue,AYValue:TChartValue; Const ALabel:String='';
                         AColor:TColor=clTeeColor):Integer; override;
    procedure Assign(Source:TPersistent); override;
    Function CalcXPos(ValueIndex:Integer):Integer; override;
    Function CalcYPos(ValueIndex:Integer):Integer; override;
    Function Clicked(x,y:Integer):Integer; override;
    procedure Delete(ValueIndex:Integer); override;
    function FirstChild(ValueIndex:Integer):Integer;
    Function IsValidSourceOf(Value:TChartSeries):Boolean; override;
    function MaxLevels:Integer;
    procedure SwapValueIndex(a,b:Integer); override;
    Function UseAxis:Boolean; override;

    property Bounds:TRect read IBounds;
    property Item[Index:Integer]:TOrgItem read GetItem write SetItem; default;
  public
    property Format:TOrgShape read FDefault write SetDefault;
    property Items:TOrgItemCollection read FNodes write SetNodes stored IsDataStored;
    property Spacing:TSpacing read FSpacing write SetSpacing;
  end;

  TOrgSeries=class(TCustomOrgSeries)
  private
    FLineStyle : TOrgLineStyle;

    procedure SetLineStyle(const Value: TOrgLineStyle);
  protected
    Procedure AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False); override;
    procedure AssignFormatting(Source:TChartSeries); override;
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    procedure DrawAllValues; override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
  public
  published
    property LineStyle:TOrgLineStyle read FLineStyle write SetLineStyle default lsSquared;
    property ShowInLegend default False;

    property Format;
    property Items;
    property Spacing;

    property XValues;
    property YValues;

    { Published declarations }
    property Active;
    property ColorSource;
    property Cursor;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;
    property PercentFormat;
    property SeriesColor;
    property Title;
    property Transparency;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    property Brush;
    property Pen;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;
  end;

implementation

uses
  {$IFDEF FMX}
  FMXTee.ProConstants
  {$ELSE}
  TeeProCo
  {$ENDIF};

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

  NotMandatoryValueList.Order:=loNone;
  CalcVisiblePoints:=False;

  FNodes:=TOrgItemCollection.Create(Self,TOrgItem);

  FDefault:=TOrgShape.Create(nil);
  FDefault.TextAlignment:=taCenter;
  FDefault.IOwner:=Self;

  FSpacing:=TSpacing.Create;
  FSpacing.ISeries:=Self;

  Pen.EndStyle:=esFlat;
  ShowInLegend:=False;
end;

Destructor TCustomOrgSeries.Destroy;
begin
  FreeAndNil(FDefault);
  FreeAndNil(FNodes);
  FSpacing.Free;
  inherited;
end;

function TCustomOrgSeries.Add(const Text:String; Superior:Integer=-1):Integer;
begin
  result:=AddXY(Superior,Count,Text);
end;

Function TCustomOrgSeries.AddXY(Const AXValue,AYValue:TChartValue; Const ALabel:String='';
                          AColor:TColor=clTeeColor):Integer;
begin
  result:=inherited AddXY(AXValue,AYValue,ALabel,AColor);

  while Items.Count<Count do
        FNodes.Add;
end;

procedure TCustomOrgSeries.AssignFormatting(Source: TChartSeries);
begin
  if Source is TCustomOrgSeries then
  with TCustomOrgSeries(Source) do
  begin
    Self.FDefault.Assign(FDefault);
    Self.FSpacing.FVert:=FSpacing.FVert;
    Self.FSpacing.FHoriz:=FSpacing.FHoriz;
  end;

  inherited;
end;

procedure TCustomOrgSeries.Assign(Source: TPersistent);
begin
  // Important: Call inherited before anything else, to avoid losing the nodes.
  inherited;

  if Source is TCustomOrgSeries then
     FNodes.Assign(TCustomOrgSeries(Source).FNodes);
end;

function TCustomOrgSeries.GetChilds(Superior:Integer; VisibleOnly:Boolean):TIntArray;
var L,
    t : Integer;
begin
  result:=nil;
  L:=0;

  for t:=0 to Min(FNodes.Count,Count)-1 do
    if (FNodes[t].Superior=Superior) and
       ((not VisibleOnly) or FNodes[t].ActiveFormat.Visible) then
    begin
      SetLength(result,L+1);
      result[L]:=t;
      Inc(L);
    end;
end;

function TCustomOrgSeries.MaxLevels: Integer;

  function CalcMaxLevel(ASuperior:Integer):Integer;
  var tmp : TIntArray;
      tmpLevel,
      t   : Integer;
  begin
    result:=0;

    tmp:=GetChilds(ASuperior,True);

    if Length(tmp)>0 then
    begin
      tmpLevel:=0;

      for t:=Low(tmp) to High(tmp) do
          tmpLevel:=Max(tmpLevel,CalcMaxLevel(tmp[t]));

      result:=1+result+tmpLevel;
    end;
  end;

begin
  result:=CalcMaxLevel(-1);
end;

type
  TFormatAccess=class(TTextShape);
  TCustomTextShapeAccess=class(TCustomTextShape);

procedure TOrgSeries.DrawAllValues;

  procedure DrawChilds(const List:TIntArray; XPos,YPos:Integer);

    function GetTreeWidth(const List:TIntArray):Integer;
    var t : Integer;
        tmp : Integer;
    begin
      result:=Spacing.Horizontal*High(List);

      tmp:=0;

      for t:=Low(List) to High(List) do
      with TOrgItem(FNodes.Items[List[t]]) do
           Inc(result, {$IFDEF FMX}Round{$ENDIF}(Max(ShapeBounds.Right-ShapeBounds.Left+1,tmp)));
    end;

    function ChildCount(Index:Integer):Integer;
    var t : Integer;
    begin
      result:=0;

      for t:=0 to Count-1 do
          if NotMandatoryValueList.Value[t]=Index then
             Inc(result);
    end;

  var tmpR : TRect;
      maxH,
      maxW,
      tmpLeft,
      tmp,
      tmpSup,
      t : Integer;

      tmpX0,
      tmpX1,
      tmpX,
      tmpY : TCoordinate;

      tmpRoots : TIntArray;

      tmpFormat : TCustomTextShape;
  begin
    {$IFNDEF D7}
    tmpRoots:=nil;
    {$ENDIF}

    // Calc total width
    maxW:=GetTreeWidth(List);

    IBounds.Top:=Min(IBounds.Top,YPos);

    for t:=Low(List) to High(List) do
    with TOrgItem(FNodes.Items[List[t]]) do
    begin
      tmp:=Height;
      ShapeBounds.Top:=YPos+ParentChart.View3DOptions.VertOffset;
      ShapeBounds.Bottom:=ShapeBounds.Top+tmp;

      if Assigned(FFormat) then
         FFormat.ShapeBounds:=ShapeBounds;
    end;

    tmpLeft:=XPos-(maxW div 2);

    IBounds.Left:=Min(IBounds.Left,tmpLeft);

    maxH:=0;

    tmpX0:=0;
    tmpX1:=0;

    tmpY:=0;

    for t:=Low(List) to High(List) do
    with TOrgItem(FNodes.Items[List[t]]) do
    begin
      tmp:=Width;

      ShapeBounds.Left:=tmpLeft;
      ShapeBounds.Right:=ShapeBounds.Left+tmp;

      if Assigned(FFormat) then
         FFormat.ShapeBounds:=ShapeBounds;

      tmpR:=ShapeBounds;

      if t=0 then
         tmpX0:=tmpLeft+((tmpR.Right-tmpR.Left) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF})
      else
      if t=High(List) then
         tmpX1:=tmpLeft+((tmpR.Right-tmpR.Left) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});

      tmpSup:=Round(NotMandatoryValueList.Value[List[t]]);

      if tmpSup<>-1 then
      begin
        ParentChart.Canvas.AssignVisiblePen(Pen);

        if LineStyle=lsSquared then
        begin
          if ChildCount(tmpSup)=1 then
             tmpY:=CalcYPos(tmpSup)
          else
             tmpY:=ShapeBounds.Top-(Spacing.Vertical div 2);

          ParentChart.Canvas.DoVertLine(tmpLeft+((tmpR.Right-tmpR.Left) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}),
                                         tmpY,ShapeBounds.Top)
        end
        else
        begin
          if ChildCount(tmpSup)=1 then
             tmpX:=CalcXPos(tmpSup)
          else
             tmpX:=tmpLeft+((tmpR.Right-tmpR.Left) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});

          ParentChart.Canvas.Line(tmpX,ShapeBounds.Top,CalcXPos(tmpSup),CalcYPos(tmpSup));
        end;
      end;

      (* Pending: how to use Hover Pen only?
      if PrepareHoverPen(List[t]) then
         tmpFormat:=Selected.Hover
      else
      *)
         tmpFormat:=ActiveFormat;

      tmpFormat.Text:=Text;
      TCustomTextShapeAccess(tmpFormat).INumLines:=NumTextLines;

      TFormatAccess(tmpFormat).Draw(ParentChart,ShapeBounds,0,0);

      tmpFormat.Text:='';

      // Workaround: Use Hover Pen only:
      if PrepareHoverPen(List[t]) then
      begin
        ParentChart.Canvas.Brush.Style:=bsClear;
        ParentChart.Canvas.Rectangle(ShapeBounds);
      end;

      maxH:={$IFDEF FMX}Round{$ENDIF}(Math.Max(maxH,tmpR.Bottom-tmpR.Top+1));

      Inc(tmpLeft,{$IFDEF FMX}Round{$ENDIF}(tmpR.Right-tmpR.Left+1));

      if t<High(List) then
         Inc(tmpLeft,Spacing.Horizontal)
      else
      with tmpFormat.Shadow do
      if Visible and (HorizSize>0) then
         Inc(tmpLeft,HorizSize);

      with tmpFormat.Shadow do
      if Visible and (VertSize>0) then
         IBounds.Bottom:=Max(IBounds.Bottom,tmpR.Bottom+VertSize)
      else
         IBounds.Bottom:=Max(IBounds.Bottom,tmpR.Bottom);
    end;

    IBounds.Right:=Max(IBounds.Right,tmpLeft);

    if LineStyle=lsSquared then
      if Length(List)>1 then
      begin
        ParentChart.Canvas.AssignVisiblePen(Pen);
        ParentChart.Canvas.DoHorizLine(tmpX0,tmpX1,tmpY);
      end;

    for t:=Low(List) to High(List) do
    begin
      tmpR:=TOrgItem(FNodes.Items[List[t]]).ShapeBounds;

      XPos:={$IFDEF FMX}Round{$ENDIF}(tmpR.Left+tmpR.Right) div 2;

      tmpRoots:=GetChilds(List[t],True);

      if Length(tmpRoots)>0 then
      begin
        if Length(tmpRoots)>1 then
        if LineStyle=lsSquared then
        begin
          ParentChart.Canvas.AssignVisiblePen(Pen);
          tmpY:=tmpR.Bottom;
          ParentChart.Canvas.DoVertLine(XPos,tmpY,YPos+maxH+(Spacing.Vertical div 2)+ParentChart.View3DOptions.VertOffset);
        end;

        DrawChilds(tmpRoots,XPos,YPos+maxH+Spacing.Vertical);
      end;
    end;
  end;

var tmpBlend : TTeeBlend;
begin
  with IBounds do
  begin
    Top:=MaxLongint;
    Left:=MaxLongint;
    Right:=-MaxLongint;
    Bottom:=-MaxLongint;
  end;

  AutoSizeNodes;

  if Transparency>0 then
     tmpBlend:=ParentChart.Canvas.BeginBlending(TeeZeroRect, Transparency)
  else
     tmpBlend:=nil;

  DrawChilds(GetChilds(-1,True),
             ParentChart.ChartXCenter+ParentChart.View3DOptions.HorizOffset,
             {$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartRect.Top));

  if Transparency>0 then
     ParentChart.Canvas.EndBlending(tmpBlend);
end;

procedure TCustomOrgSeries.AutoSizeNodes;

  procedure DoCalcBounds(const AItem:TOrgItem; const AFormat:TTextShape);
  begin
    AFormat.Text:=AItem.Text;
    TFormatAccess(AFormat).CalcBounds(ParentChart);

    // Remember INumLines and ShapeBounds global format values, into each local AItem:
    AItem.NumTextLines:=TFormatAccess(AFormat).INumLines;

    AItem.ShapeBounds:=AFormat.ShapeBounds;
  end;

var t : Integer;
    tmp : TTextShape;
begin
  for t:=0 to Min(FNodes.Count,Count)-1 do
  begin
    tmp:=FNodes[t].ActiveFormat;

    if tmp.AutoSize then
    begin
      tmp.Text:=XLabel[t];
      DoCalcBounds(FNodes[t],tmp);
      tmp.Text:='';
    end
    else
    if not Assigned(FNodes[t].FFormat) then
       // First time in "no AutoSize" mode, set initial size (ShapeBounds)
       DoCalcBounds(FNodes[t],FNodes[t].Format);
  end;
end;

Function TCustomOrgSeries.Clicked(x,y:Integer):Integer;
var t : Integer;
begin
  result:=TeeNoPointClicked;

  for t:=0 to Min(FNodes.Count,Count)-1 do
  if PointInRect(FNodes[t].ShapeBounds,x,y) then
  begin
    result:=t;
    break;
  end;
end;

function TCustomOrgSeries.UseAxis: Boolean;
begin
  result:=False;
end;

function TCustomOrgSeries.IsValidSourceOf(Value: TChartSeries): Boolean;
begin
  result:=Value is TCustomOrgSeries;
end;

procedure TOrgSeries.AddSampleValues(NumValues: Integer;
  OnlyMandatory: Boolean);

const
  MaxNames=8;

  Names:Array[0..MaxNames] of String=
    ('John','Anne','Mary','Paul','Bob','Mike','Lisa','Brad','Peter');

  SurNames:Array[0..MaxNames] of String=
    ('Smith','Shane','Wizard','Smart','Best','Patson','Hood','Dale','Scarlet');

  function RandomName:String;
  begin
    result:=Names[Random(MaxNames+1)]+' '+SurNames[Random(MaxNames+1)]+TeeLineSeparator;
  end;

var tmp,
    tmp1 : Integer;
begin
  tmp:=Add(RandomName+'President');
         Add(RandomName+'Sales Director',tmp);
   tmp1:=Add(RandomName+'Product Manager'+TeeLineSeparator+'USA',tmp);
          Add(RandomName+'Asian Sales',tmp1);
   tmp1:=Add(RandomName+'Human Relations',tmp);
          Add(RandomName+'Assistant',tmp1);
end;

procedure TCustomOrgSeries.SetSpacing(const Value: TSpacing);
begin
  FSpacing.Assign(Value);
end;

procedure TCustomOrgSeries.ClearLists;
begin
  inherited;

  if not (csLoading in ComponentState) then
     if Assigned(FNodes) then
        FNodes.Clear;
end;

procedure TCustomOrgSeries.SwapValueIndex(a,b:Integer);
var tmp : TCollectionItem;
begin
  inherited;
  tmp:=FNodes.Items[b];
  FNodes.Items[a].Index:=b;
  tmp.Index:=a;
end;

procedure TCustomOrgSeries.Loaded;
begin
  inherited;

  if Assigned(FNodes) then
  begin
    while FNodes.Count>Count do
          FNodes.Delete(FNodes.Count-1);

    while FNodes.Count<Count do
          FNodes.Add;
  end;
end;

function TCustomOrgSeries.FirstChild(ValueIndex:Integer):Integer;
begin
  for result:=0 to Count-1 do
  if NotMandatoryValueList.Value[result]=ValueIndex then
     Exit;

  result:=-1;
end;

procedure TCustomOrgSeries.Delete(ValueIndex:Integer);
var tmp : Integer;
begin
  // Cascade deleting of child nodes, recursive
  repeat
    tmp:=FirstChild(ValueIndex);
    if tmp<>-1 then
       Delete(tmp);
  until tmp=-1;

  inherited;

  if FNodes.Count>ValueIndex then
     {$IFDEF D18}
     FNodes[ValueIndex].DisposeOf;
     {$ELSE}
     FNodes[ValueIndex].Free;
     {$ENDIF}

  with NotMandatoryValueList do
  for tmp:=0 to Count-1 do
  if Value[tmp]>ValueIndex then
     Value[tmp]:=Value[tmp]-1;
end;

class function TCustomOrgSeries.GetEditorClass: String;
begin
  result:='TOrgSeriesEditor';
end;

procedure TCustomOrgSeries.SetNodes(const Value: TOrgItemCollection);
begin
  FNodes.Assign(Value);
end;

function TCustomOrgSeries.GetItem(Index: Integer): TOrgItem;
begin
  result:=FNodes.Items[Index];
end;

procedure TCustomOrgSeries.SetDefault(const Value: TOrgShape);
begin
  FDefault.Assign(Value);
end;

procedure TCustomOrgSeries.SetItem(Index: Integer; const Value: TOrgItem);
begin
  FNodes.Items[Index]:=Value;
end;

procedure TCustomOrgSeries.SetParentChart(const Value: TCustomAxisPanel);
var t : Integer;
begin
  inherited;

  if Assigned(FNodes) then
  for t:=0 to Items.Count-1 do
      with Items[t] do
      if Assigned(FFormat) then
         FFormat.ParentChart:=Value;
end;

function TCustomOrgSeries.CalcXPos(ValueIndex: Integer): Integer;
begin
  with FNodes[ValueIndex].ShapeBounds{$IFDEF FMX}.Round{$ENDIF} do
       result:=(Left+Right) div 2;
end;

function TCustomOrgSeries.CalcYPos(ValueIndex: Integer): Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(FNodes[ValueIndex].ShapeBounds.Bottom);
end;

Function TCustomOrgSeries.CheckMouse(x,y:Integer):Boolean;
var tmp : Integer;
begin
  result:=False;

  tmp:=Clicked(x,y);

  if tmp<>TeeNoPointClicked then
  with Item[tmp].ActiveFormat do
  if Cursor<>crDefault then
  begin
    ParentChart.Cursor:=Cursor;
    result:=True;
  end;

  if not result then
     result:=inherited CheckMouse(x,y);
end;

{ TOrgSeries }
procedure TOrgSeries.SetLineStyle(const Value: TOrgLineStyle);
begin
  if FLineStyle<>Value then
  begin
    FLineStyle:=Value;
    Repaint;
  end;
end;

procedure TOrgSeries.AssignFormatting(Source:TChartSeries);
begin
  if Source is TOrgSeries then
     FLineStyle:=TOrgSeries(Source).FLineStyle;

  inherited;
end;

{ TSpacing }
Constructor TSpacing.Create;
begin
  inherited;
  FHoriz:=8;
  FVert:=16;
end;

procedure TSpacing.Assign(Source: TPersistent);
begin
  if Source is TSpacing then
  with TSpacing(Source) do
  begin
    Self.FHoriz:=FHoriz;
    Self.FVert:=FVert;
  end
  else
    inherited;
end;

procedure TSpacing.SetHoriz(const Value: Integer);
begin
  if FHoriz<>Value then
  begin
    FHoriz:=Value;
    ISeries.Repaint;
  end;
end;

procedure TSpacing.SetVert(const Value: Integer);
begin
  if FVert<>Value then
  begin
    FVert:=Value;
    ISeries.Repaint;
  end;
end;

{ TOrgItem }
Destructor TOrgItem.Destroy;
begin
  RemoveFormat;
  inherited;
end;

procedure TOrgItem.Assign(Source: TPersistent);
begin
  if Source is TOrgItem then
  begin
    Format:=TOrgItem(Source).FFormat;

    // ?? Superior:=TOrgItem(Source).Superior;
  end
  else
     inherited;
end;

procedure TOrgItem.RemoveFormat;
begin
  if Assigned(FFormat) then
  begin
    FreeAndNil(FFormat);
    Series.Repaint;
  end;
end;

type
  TOwnedCollectionAccess=class(TOwnedCollection);

function TOrgItem.Series:TCustomOrgSeries;
begin
  result:=TCustomOrgSeries(TOwnedCollectionAccess(Collection).GetOwner);
end;

procedure TOrgItem.SetFormat(const Value: TOrgShape);
begin
  if Assigned(Value) then
  begin
    Format.Assign(Value);
    Format.Text:=Text;
  end
  else
    FreeAndNil(FFormat);
end;

function TOrgItem.GetText: String;
begin
  result:=Series.XLabel[Index];
end;

procedure TOrgItem.SetText(const Value: String);
begin
  Series.XLabel[Index]:=Value;
end;

function TOrgItem.AddChild(const Text: String): TOrgItem;
begin
  result:=Series[Series.Add(Text,Index)];
end;

function TOrgItem.AddBrother(const Text: String): TOrgItem;
begin
  result:=Series[Series.Add(Text,Superior)];
end;

function TOrgItem.GetSuperior: Integer;
begin
  result:=Round(Series.NotMandatoryValueList[Index]);
end;

procedure TOrgItem.SetSuperior(const Value: Integer);
begin
  Series.NotMandatoryValueList[Index]:=Value;
end;

function TOrgItem.GetFormat: TOrgShape;
begin
  if not Assigned(FFormat) then
  begin
    FFormat:=TOrgShape.Create(nil);
    FFormat.Assign(Series.FDefault);
    FFormat.ParentChart:=Series.ParentChart;
    FFormat.ShapeBounds:=ShapeBounds;
  end;

  result:=FFormat;
end;

function TOrgItem.HasFormat: Boolean;
begin
  result:=Assigned(FFormat);
end;

function TOrgItem.Parent:TOrgItem;
begin
  if (Superior=-1) or (Superior>=Series.Count) then
     result:=nil
  else
     result:=Series.Item[Superior];
end;

function TOrgItem.Level: Integer;
var tmp : TOrgItem;
begin
  result:=0;
  tmp:=Parent;

  while Assigned(tmp) and (tmp<>tmp.Parent) do
  begin
    Inc(result);
    tmp:=tmp.Parent;
  end;
end;

{ TOrgItemCollection }
function TOrgItemCollection.Get(Index: Integer): TOrgItem;
begin
  result:=TOrgItem(inherited Items[Index]);
end;

procedure TOrgItemCollection.Put(Index: Integer; const Value: TOrgItem);
begin
  inherited Items[Index]:=Value;
end;

function TOrgItem.ActiveFormat: TOrgShape;
begin
  if Assigned(FFormat) then
     result:=FFormat
  else
     result:=Series.FDefault;
end;

function TOrgItem.GetWidth: Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Right-ShapeBounds.Left);
end;

procedure TOrgItem.SetWidth(const Value: Integer);
begin
  if Width<>Value then
  begin
    ShapeBounds.Right:=ShapeBounds.Left+Value;
    Series.Repaint;
  end;
end;

function TOrgItem.GetHeight: Integer;
begin
  result:={$IFDEF FMX}Round{$ENDIF}(ShapeBounds.Bottom-ShapeBounds.Top);
end;

procedure TOrgItem.SetHeight(const Value: Integer);
begin
  if Height<>Value then
  begin
    ShapeBounds.Bottom:=ShapeBounds.Top+Value;
    Series.Repaint;
  end;
end;

{ TOrgShape }

class Procedure TOrgSeries.CreateSubGallery(AddSubChart:TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Lines);
  AddSubChart(TeeMsg_Transparency);
end;

procedure TOrgShape.Repaint;
begin
  if Assigned(IOwner) then
     IOwner.Repaint
  else
     inherited;
end;

class Procedure TOrgSeries.SetSubGallery(ASeries:TChartSeries; Index:Integer);
begin
  case Index of
    1: TOrgSeries(ASeries).LineStyle:=lsDiagonal;
    2: TOrgSeries(ASeries).Format.Transparent:=True;
  else
     inherited;
  end;
end;

initialization
  RegisterTeeSeries( TOrgSeries, @TeeMsg_GalleryOrgSeries,
                                 @TeeMsg_GalleryExtended, 1);
finalization
  UnRegisterTeeSeries([TOrgSeries]);
end.

