unit NAryC;
//*******************************************************
// N-ary tree class.
//*******************************************************
// Copyright (C) 1998 John Wiley & Sons, Inc.
// All rights reserved. See additional copyright
// information in Readme.txt.
//
// Revised (C) 1999 Andrzej Grażyński, HELION Publ.
//
//*******************************************************

interface

uses
    Windows, Graphics, Math, Classes;

type
    String10 = String[10];
    PNAryNodeArray = ^TNAryNodeArray;
    TNAryNode = class(TObject)
        private
        public
            Id                    : String10;
            ParentNode            : TNAryNode;
            NumChildren           : Integer;
            Children             : Array of TNAryNode;
            Position              : TPoint;

            destructor Destroy; override;
            procedure SetPosition(var xmin : Integer; ymin : Integer);
            procedure DrawNode(cvs : TCanvas; selected : TNAryNode);
            procedure DrawSubtree(cvs : TCanvas; selected : TNAryNode);
            function NodeAtPoint(X, Y : Integer) : TNAryNode;
            procedure AddChild(new_parent : TNAryNode; new_id : String);
            procedure RemoveChild(target : TNAryNode);
    end;
    TNAryNodeArray = array [1..100000000] of TNAryNode;

implementation

const
    WID = 40;
    HGT = 16;
    HGAP = 2;
    VGAP = 6;

// Free any children.
destructor TNAryNode.Destroy;
var
    i : Integer;
begin
    for i := 1 to NumChildren do
      Children[i-1].Free;
    inherited Destroy;
end;

// Set the position for the node and its descendants.
// Update xmin so it indicates the rightmost position
// used by the node and its descendants.
procedure TNAryNode.SetPosition(var xmin : Integer; ymin : Integer);
var
    i : Integer;
begin
    Position.Y := ymin;

    if (NumChildren < 1) then begin
        // Place the node at (xmin, ymin).
        Position.X := xmin;
        xmin := xmin + WID;
    end else begin
        // Position the children.
        for i := 1 to NumChildren do
        begin
            Children[i-1].SetPosition(xmin, ymin + HGT + VGAP);
            if (i < NumChildren) then xmin := xmin + HGAP;
        end;

        // Center the node over its children.
        Position.X := (
            Children[1-1].Position.X +
            Children[NumChildren-1].Position.X) div 2;
    end;
end;

// Draw this node.
procedure TNAryNode.DrawNode(cvs : TCanvas; selected : TNAryNode);
var
    rect      : TRect;
    text_size : TSize;
begin
    // Select appropriate colors.
    if (selected = Self) then
    begin
        cvs.Font.Color := clWhite;
        cvs.Brush.Color := clBlack;
    end else begin
        cvs.Font.Color := clBlack;
        cvs.Brush.Color := clWhite;
    end;

    // Erase the spot and draw a box around it.
    rect.Left   := Position.X;
    rect.Right  := Position.X + WID;
    rect.Top    := Position.Y;
    rect.Bottom := Position.Y + HGT;
    cvs.FillRect(rect);
    cvs.Rectangle(rect.Left, rect.Top, rect.Right, rect.Bottom);

    // Draw the label.
    text_size := cvs.TextExtent(Id);
    cvs.TextOut(
        (rect.Left + rect.Right - text_size.cx) div 2,
        (rect.Top + rect.Bottom - text_size.cy) div 2,
        Id);
end;

// Draw the subtree rooted at this node.
procedure TNAryNode.DrawSubtree(cvs : TCanvas; selected : TNAryNode);
var
    i : Integer;
begin
    // Draw this node.
    DrawNode(cvs, selected);

    // Draw the children.
    for i := 1 to NumChildren do
    begin
        Children[i-1].DrawSubtree(cvs, selected);
        cvs.MoveTo(
            Position.X + WID div 2,
            Position.Y + HGT);
        cvs.LineTo(
            Children[i-1].Position.X + WID div 2,
            Children[i-1].Position.Y);
    end;
end;

// Find the descendant node containing this point.
// Note that this is not a very sophisticated method.
// A quadtree would be much faster.
function TNAryNode.NodeAtPoint(X, Y : Integer) : TNAryNode;
var
    i : Integer;
begin
    Result := nil;

    // Check this node.
    if ((Position.X <= X) and (X <= Position.X + WID) and
        (Position.Y <= Y) and (Y <= Position.Y + HGT))
    then
        Result := Self
    else begin
        // Check the children.
        for i := 1 to NumChildren do
        begin
            Result := Children[i-1].NodeAtPoint(X, Y);
            if (Result <> nil) then break;
        end;
    end;
end;

// Add a child to this node.
procedure TNAryNode.AddChild(new_parent : TNAryNode; new_id : String);
begin

 (*
    // Make room for the new child.
    // Create the new array.
    GetMem(new_array, (NumChildren + 1) * SizeOf(TNAryNode));

    // Copy the children into the new array.
    for i := 1 to NumChildren do
        new_array^[i] := Children^[i];




    // Free the previously allocated memory.
    if (NumChildren > 0) then FreeMem(Children);
    Children := new_array;

 *)
     SetLength(Children, NumChildren + 1);




    // Create the new child.
    NumChildren := NumChildren + 1;
    Children[NumChildren-1] := TNAryNode.Create;
    Children[NumChildren-1].Id := new_id;
    Children[NumChildren-1].ParentNode := new_parent;
end;

// Usuń potomka
procedure TNAryNode.RemoveChild(target : TNAryNode);
var
    num, i : Integer;
begin
    // Znajdź potomka
    for num := 1 to NumChildren do
    begin
        if (Children[num-1] = target) then break;
    end;

    if (num > NumChildren) then
        raise EInvalidOperation.Create(
            'Wskazany węzeł nie jest potomkiem tego węzła');

    // zwolnij potomka
    Children[num-1].Free;

    // Zsuń pozycje w tablicy wskaźników
    for i := num + 1 to NumChildren do
         Children[i - 1-1] := Children[i-1];
    Children[NumChildren-1] := nil;
    NumChildren := NumChildren - 1;
end;

end.
