//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "VmxCustomTreeView.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck(TVmxCustomTreeView *)
{
        new TVmxCustomTreeView(NULL);
}
//---------------------------------------------------------------------------

TmTreeItem::TmTreeItem ()
{
  NChild = 0;
  Parent = NULL;
}

TmTreeItem* TmTreeItem::AddChild (AnsiString T)
{
  if (NChild < 1024)
  {
    Childs[NChild]=new TmTreeItem;
    Childs[NChild]->Text=T;
    Childs[NChild]->Parent=this;
    NChild++;
    return Childs[NChild-1];
  }
  return NULL;
}

void __fastcall TmTreeItem::Assign (TmTreeItem *Item)
{
  Checked = Item->Checked;
  Hovered = Item->Hovered;
  Expanded = Item->Expanded;
  Text = Item->Text;
  Parent = Item->Parent;
  NChild = Item->NChild;
  for (int i = 0; i < NChild; i++)
    Childs[i]=Item->Childs[i];
  for (int i = NChild; i < 1024; i++)
    Childs[i]=NULL;
}

__fastcall TVmxCustomTreeView::TVmxCustomTreeView(TComponent* Owner)
        : TGraphicControl(Owner)
{
  ICount = 0;
}
//---------------------------------------------------------------------------

TmTreeItem* __fastcall TVmxCustomTreeView::Add (AnsiString T)
{
  if (ICount < 1024)
  {
    Items[ICount] = new TmTreeItem;
    Items[ICount] -> Text = T;
    ICount++;
    return Items[ICount-1];
  }
  return NULL;
}

#define ROUND(a) ((int)(a+0.5))

void __fastcall TVmxCustomTreeView::Paint (void)
{
  Canvas->Brush->Color=Color;
  Canvas->FillRect(TRect(0,0,Width,Height));
  int y = 4-FScrollPos*(Canvas->TextHeight("B")+3), my = 0, ny = y;
  Canvas->Font->Assign(Font);
  for (int i = 0; i < ICount; i++)
    AnalyzeDraw (Items[i],4,y,my);
  int osm = ScrollMax;
  if (y+FScrollPos*(Canvas->TextHeight("B")+3)>Height)
    ScrollMax = (y+FScrollPos*(Canvas->TextHeight("B")+3)-Height)/(Canvas->TextHeight("B")+3);
  else if (y+FScrollPos*(Canvas->TextHeight("B")+3)<=Height)
  {
    ScrollMax=-1;
    FScrollPos=0;
    int y2 = 4, my2 = 0;
    for (int i = 0; i < ICount; i++)
      AnalyzeDraw (Items[i],4,y2,my2);
  }
  if (ScrollMax != osm && FOnChangeScrollMax!=NULL)
    FOnChangeScrollMax (this);
}

void __fastcall TVmxCustomTreeView::MouseDown (TMouseButton Button, TShiftState Shift, int X, int Y)
{
  int y = 4-FScrollPos*(Canvas->TextHeight("B")+3);
  for (int i = 0; i < ICount; i++)
    AnalyzeC (Items[i],4,y,X,Y);
}

void __fastcall TVmxCustomTreeView::MouseMove (TShiftState Shift, int X, int Y)
{
  int y = 4-FScrollPos*(Canvas->TextHeight("B")+3);
  for (int i = 0; i < ICount; i++)
    AnalyzeH (Items[i],4,y,X,Y);
}

void __fastcall TVmxCustomTreeView::SetScrollPos (int New)
{
  if (New < 0) return;
  FScrollPos = New;
  Paint ();
}

void __fastcall CheckSubItems (TmTreeItem *Item, bool New)
{
  Item->Checked=New;
  for (int i = 0; i < Item->NChild; i++)
    CheckSubItems (Item->Childs[i],New);
}

void __fastcall TVmxCustomTreeView::AnalyzeC (TmTreeItem *Item, int x, int &y, int X, int Y)
{
  int TH = Canvas -> TextHeight (Item->Text);
  if (PtInRect (TRect(x+(Item->NChild>0?13:0)+(FChecks?15:0),y,x+(FChecks?15:0)+(Item->NChild>0?13:0)+Canvas->TextWidth(Item->Text),y+TH), TPoint(X,Y)))
  {
    Item->Hovered=false;
    Paint ();
    if (FOnClickItem!=NULL)
      FOnClickItem(this,Item);
    y+=TH+3;
    return;
  }
  else if (FChecks && PtInRect (TRect (x+(Item->NChild>0?13:0),y+TH/2-6,x+(Item->NChild>0?13:0)+12,y+TH/2+6), TPoint (X, Y)))
  {
    Item->Checked=!Item->Checked;
    for (int i = 0; i < Item->NChild; i++)
      CheckSubItems (Item->Childs[i],Item->Checked);
    Paint ();
  }
  if (Item->NChild>0)
  {
    if (PtInRect(TRect(x,y+TH/2-4.5,x+9,y+TH/2+4.5),TPoint(X,Y)))
    {
      Item->Expanded=!Item->Expanded;
      Paint();
      y+=TH+3;
      return;
    }
    else
    {
      if (Item->Expanded)
      {
        y+=TH+3;
        for (int i = 0; i < Item->NChild; i++)
          AnalyzeC (Item->Childs[i],x+19,y,X,Y);
        return;
      }
    }
  }
  y+=TH+3;
}

void __fastcall TVmxCustomTreeView::AnalyzeH (TmTreeItem *Item, int x, int &y, int X, int Y)
{
  int TH = Canvas -> TextHeight (Item->Text);
  bool oh = Item->Hovered;
  if (PtInRect (TRect(x+(Item->NChild>0?13:0)+(FChecks?15:0),y,x+(Item->NChild>0?13:0)+(FChecks?15:0)+Canvas->TextWidth(Item->Text),y+TH), TPoint(X,Y)))
  {
    Item->Hovered=true;
    if (Item->Hovered!=oh)
      Paint();
    y+=TH+3;
    return;
  }
  else Item->Hovered=false;
  if (Item->Hovered!=oh)
    Paint();
  if (Item->NChild>0)
  {
    if (Item->Expanded)
    {
      y+=TH+3;
      for (int i = 0; i < Item->NChild; i++)
        AnalyzeH (Item->Childs[i],x+19,y,X,Y);
      return;
    }
  }
  y+=TH+3;
}

void __fastcall TVmxCustomTreeView::SetLC (TColor New)
{
  FLC = New;
  Invalidate ();
}

void __fastcall CheckBox (TCanvas *Canvas, int x, int y)
{
  Canvas->MoveTo(x,y+3);
  Canvas->LineTo(x,y+5);
  Canvas->MoveTo(x+1,y+4);
  Canvas->LineTo(x+1,y+6);
  Canvas->MoveTo(x+2,y+5);
  Canvas->LineTo(x+2,y+7);
  Canvas->MoveTo(x+3,y+4);
  Canvas->LineTo(x+3,y+6);
  Canvas->MoveTo(x+4,y+3);
  Canvas->LineTo(x+4,y+5);
  Canvas->MoveTo(x+5,y+2);
  Canvas->LineTo(x+5,y+4);
  Canvas->MoveTo(x+6,y+1);
  Canvas->LineTo(x+6,y+3);
  Canvas->MoveTo(x+7,y);
  Canvas->LineTo(x+7,y+2);
}

void __fastcall TVmxCustomTreeView::AnalyzeDraw (TmTreeItem *Item, int x, int &y, int &My)
{
  int TH = Canvas -> TextHeight (Item->Text);
  int tx = x+(Item->NChild>0?13:0)+(FChecks?15:0);
  Canvas -> Pen -> Color = FLC;
  Canvas->TextOutA(tx-(Item->Hovered?1:0),y-(Item->Hovered?1:0),Item->Text);
  if (FChecks)
  {
    Canvas -> Rectangle (x+(Item->NChild>0?13:0),y+TH/2-6,x+(Item->NChild>0?13:0)+12,y+TH/2+6);
    if (Item->Checked)
      CheckBox (Canvas, x+(Item->NChild>0?13:0)+2, y+TH/2-4);
  }
  if (Item->NChild>0)
  {
    Canvas -> Rectangle (x,y+TH/2-4.5,x+9,y+TH/2+4.5);
    Canvas -> MoveTo(x+2,y+TH/2-0.5);
    Canvas -> LineTo(x+7,y+TH/2-0.5);
    if (Item->Expanded)
    {
      int oy = y;
      y+=TH+3;
      My = y;
      int m;
      for (int i = 0; i < Item->NChild; i++)
      {
        AnalyzeDraw (Item->Childs[i],x+19,y,m);
        Canvas -> MoveTo (x+4,oy+TH/2+4.5);
        Canvas -> LineTo (x+4,m-TH-3+TH/2);
        Canvas -> LineTo (x+17,m-TH-3+TH/2);
      }
      return;
    }
    else
    {
      Canvas -> MoveTo (x+4,y+TH/2-4.5+2);
      Canvas -> LineTo (x+4,y+TH/2-4.5+7);
    }
  }
  y+=TH+3;
  My = y;
}

void __fastcall TVmxCustomTreeView::Clear (void)
{
  for (int i = 0; i < ICount; i++)
  {
    DelSub (Items[i]);
    delete Items[i];
    Items[i]=NULL;
  }
  ICount=0;
}

void __fastcall TVmxCustomTreeView::DelSub (TmTreeItem *Item)
{
  for (int i = 0; i < Item->NChild; i++)
  {
    DelSub(Item->Childs[i]);
    delete Item->Childs[i];
  }
  Item->NChild=0;
}

void __fastcall TVmxCustomTreeView::LoadFromFILE (FILE *fp)
{
  if (fp == NULL) return;
  Clear ();
  int n;
  fscanf (fp, "%d\n", &n);
  ICount = n;
  for (int i = 0; i < n; i++)
  {
    Items[i] = new TmTreeItem;
    LoadItem (fp,Items[i]);
  }
  Paint ();
}

AnsiString __fastcall RS (FILE *fp)
{
  char c = fgetc (fp);
  AnsiString A;
  while (c != '\n' && c != '\r' && c != '\0' && !feof (fp))
  {
    A+=c;
    c=fgetc(fp);
  }
  return A;
}

void __fastcall TVmxCustomTreeView::LoadItem (FILE *fp, TmTreeItem *Item)
{
  Item->Text=RS(fp);
  int n,j;
  fscanf (fp, "%d\n%d\n", &n, &j);
  Item->NChild=n;
  Item->Checked=(j?true:false);
  for (int i = 0; i < n; i++)
  {
    Item->Childs[i]=new TmTreeItem;
    LoadItem(fp, Item->Childs[i]);
  }
}

void __fastcall TVmxCustomTreeView::SaveItem (FILE *fp, TmTreeItem *Item)
{
  fprintf (fp, "%s\n", Item->Text.c_str());
  int n=Item->NChild,j=Item->Checked?1:0;
  fprintf (fp, "%d\n%d\n", &n, &j);
  for (int i = 0; i < n; i++)
    SaveItem(fp, Item->Childs[i]);
}

void __fastcall TVmxCustomTreeView::SetChecks (bool New)
{
  FChecks = New;
  Invalidate ();
}

bool __fastcall DelWI (TmTreeItem *Item1, TmTreeItem *DI)
{
  for (int i = 0; i < Item1->NChild; i++)
  {
    if (DI == Item1->Childs[i])
    {
      delete Item1->Childs[i];
      Item1->Childs[i]=new TmTreeItem;
      for (int j = i; j < Item1->NChild-1; j++)
        Item1->Childs[j]->Assign(Item1->Childs[j+1]);
      delete Item1->Childs[Item1->NChild-1];
      Item1->NChild--;
      return true;
    }
    else
    {
      if (DelWI (Item1->Childs[i],DI))
        return true;
    }
  }
  return false;
}

bool __fastcall TVmxCustomTreeView::Delete (TmTreeItem *Item)
{
  for (int i = 0; i < ICount; i++)
  {
    if (Items[i]==Item)
    {
      delete Items[i];
      Items[i]=new TmTreeItem;
      for (int j = i; j < ICount-1; j++)
        Items[j]->Assign(Items[j+1]);
      delete Items[ICount-1];
      ICount--;
      return true;
    }
    else
    {
      if (DelWI (Items[i],Item))
        return true;
    }
  }
  return false;
}

void __fastcall TVmxCustomTreeView::SaveToFILE (FILE *fp)
{
  if (fp == NULL) return;
  fprintf (fp, "%d\n", &ICount);
  for (int i = 0; i < ICount; i++)
    SaveItem (fp,Items[i]);
}

namespace Vmxcustomtreeview
{
        void __fastcall PACKAGE Register()
        {
                 TComponentClass classes[1] = {__classid(TVmxCustomTreeView)};
                 RegisterComponents("VMX", classes, 0);
        }
}
//---------------------------------------------------------------------------
