Jump to content
Magno

Copy nodes from a TTreeView with Delphi 4!

Recommended Posts

Yes, Delphi 4.... Let's go, if you can help.

 

I have a tree view with some nodes, like below

 

image.png.5e9aaade8305355d178b14a39e1f9e74.png

 

I need to, eg., copy the select node (1.1.1) into 1.1.1.2 !

image.png.125b85a30c3738e30a1db7662a1bcdea.png

 

I've trying to figure out how to accomplish this. I already have a copy/past function that works well if no such complex copy is required, like copy a top node into a lower child node. The recursive function goes crazy with this lol

 

I found some solutions but mostly will use newer version of Delphi so it won't work.  One of the solutions I found was that:

https://www.swissdelphicenter.ch/en/showcode.php?id=1876

 

But it won't allow me to copy into the same branch from a top level.

 

I would appreciate any help on this!

 

Share this post


Link to post

Here, you dont needs a "recursive" function!!! because that if you dont controls it, you know what happens... 🤣

  1. We can use drag-n-drop's "automatic" mode to use less code!
  2. Afterwards, we need to know which "node" was selected and dragged, for that we can use two TreeView functions: "GetHitTestInfoAt(X, Y);" and "GetNodeAt(X, Y);"
  3. After that, we need to test where the "clicks" occurred in the TreeView;
  4. And finally, decide whether we are "Inserting" or "Adding" items to an existing "Node"!
  5. However, all this will happen after the "target" accepts or rejects the "Drag-n-drop" action!

Here my sample about this situation:

  • you can drag-n-drop in the same TV or to other
  • you free for move to any place on TV: to root or any other level!
type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    procedure TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
    procedure TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  TreeView1.DragMode := TDragMode.dmAutomatic;
  TreeView1.FullExpand;
end;

procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
  Node      : TTreeNode;
  AttachMode: TNodeAttachMode;
  HT        : THitTests;
begin
  AttachMode := TNodeAttachMode.naAdd;
  //
  if TreeView1.Selected = nil then
    exit;
  //
  HT   := TreeView1.GetHitTestInfoAt(X, Y);
  Node := TreeView1.GetNodeAt(X, Y);
  //
  if ((HT - [htOnItem, htOnIcon, htNowhere, htOnIndent]) <> HT) then
    begin
      if ((htOnItem in HT) or (htOnIcon in HT)) then
        AttachMode := naAddChild
      else
        if (htNowhere in HT) then
          AttachMode := naAdd
        else
          if (htOnIndent in HT) then
            AttachMode := naInsert;
      //
      TreeView1.Selected.MoveTo(Node, AttachMode);
      //
      TreeView1.FullExpand;
    end;
end;

procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
begin
  Accept := true;
end;

end.

image.thumb.png.89a4e452d6aa07c5a70ad279b1270943.png

 

Edited by programmerdelphi2k
  • Haha 1
  • Confused 1

Share this post


Link to post

I need to do it programmatically, I will try use your idea to see if I can do it now, but I tested and was able only to move and not adding or inserting.

 

Thanks!!! 

Edited by Magno

Share this post


Link to post
5 hours ago, Magno said:

I need to do it programmatically, I will try use your idea to see if I can do it now, but I tested and was able only to move and not adding or inserting.

 

Thanks!!! 

Yeah, I think you can ignore that answer, it's really not relevant to your question. 

 

What you are trying to do is possible by iterating over the node and its children. I  would start with some code to do that iteration. Do you have such code? 

  • Like 2

Share this post


Link to post

I am attaching a simple functional demo I just did with the code I am using. It has 3 buttons, one to select the source node, other to the destination (target) and the copy button itself.

I works fine, but not if you try to copy a "father" node into it's on child, no problem to copy into another branch, like the 2nd image in the initial message here.

 

I am really happy if you can help me because I am struggling for about 2 days on it, maybe a technique to make the node "understand" where it should stop when it's into the same branch, because only seeing if the node is nil isn't enough as by the recursive method we will have always something to iterate with this specific case and it will blow the memory. Maybe casting a class into the Data, or mark its pointer memory address, I dunno what could be the best. I am still trying something here.

CopyTreeView.rar

Share this post


Link to post

Result:

image.thumb.png.c16f1feb251a0d10847cabc1fcdb797a.png

 

The code was written in D11 but I tried to keep it as simple as possible. I hope D4 supports the below calls...

Type

  TTreeNodes = Array Of TTreeNode;


procedure TForm1.Button3Click(Sender: TObject);
Var
 added: TTreeNodes;
begin
   SetLength(added, 0);

   CopyNodes(Source, Target, added);
   Target.Expanded := true;
end;


function TForm1.IsInArray(inTreeNode: TTreeNode; inArray: TTreeNodes): Boolean;
Var
 a: Integer;
begin
  Result := False;

  For a := 0 To Length(inArray) - 1 Do
    If inArray[a] = inTreeNode Then
    Begin
      Result := True;
      Exit;
    End;
end;


procedure TForm1.CopyNodes(inSource, inDestination: TTreeNode; var inCopied: TTreeNodes);
Var
 a: Integer;
 tv: TTreeView;
 tn: TTreeNode;
begin
 tv := inSource.TreeView As TTreeView;

 For a := 0 To inSource.Count - 1 Do
  Begin
    If Not IsInArray(inSource.Item[a], inCopied) Then
    Begin
      tn := tv.Items.AddChild(inDestination, inSource.Item[a].Text);

      // Do data copying, whatever

      SetLength(inCopied, Length(inCopied) + 1);
      inCopied[Length(inCopied) - 1] := tn;

      If inSource.Item[a].Count > 0 Then
        CopyNodes(inSource.Item[a], tn, inCopied);
    End;
  End;
end;

 

  • Thanks 1

Share this post


Link to post
3 hours ago, aehimself said:

Result:

image.thumb.png.c16f1feb251a0d10847cabc1fcdb797a.png

 

The code was written in D11 but I tried to keep it as simple as possible. I hope D4 supports the below calls...

 

Worked like a charm!!! I didn't though using arrays lol.. I love programming because of this! I will convert back to my real code. I just want to check all the behavior, but so far so good.

 

Thank you very much for you time!!

 

3 hours ago, aehimself said:

 

 

Share this post


Link to post

A TList would be better choice but I'm not sure D4 has it... that's why I settled with an array.

Share this post


Link to post
On 12/16/2022 at 3:09 PM, aehimself said:

A TList would be better choice but I'm not sure D4 has it... that's why I settled with an array.

Yes I has and I thought to move to a TList, but this way is working too! Thank you!!

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×