Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 205
  • Last Modified:

AGAIN! For mullet_attack.

I've updated the code to do what I want... been when it gets to the point when there are 2 sites without folders, the second site will not save and I get and access violation... Here is my form's code:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, CoolCtrls, ComCtrls, Menus, CorelButton, inifiles;

type
TNodeStyle = (nsFolder, nsSite);
   
  PNodeInfo = ^TNodeInfo;
  TNodeInfo = record
    NodeStyle : TNodeStyle;
    Option1 : string;
    Option2 : string;
    Option3 : string;
    Option4 : string;
    Option5 : string;
    Option6 : string;
end;

  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    CoolEdit1: TCoolEdit;
    CoolEdit2: TCoolEdit;
    CoolEdit3: TCoolEdit;
    CoolEdit4: TCoolEdit;
    CoolEdit5: TCoolEdit;
    CoolMemo1: TCoolMemo;
    PopupMenu1: TPopupMenu;
    NewSite1: TMenuItem;
    NewFolder1: TMenuItem;
    N1: TMenuItem;
    EditSite1: TMenuItem;
    Rename1: TMenuItem;
    N2: TMenuItem;
    Delete1: TMenuItem;
    CorelButton1: TCorelButton;
    CorelButton2: TCorelButton;
    CorelButton3: TCorelButton;
    Button1: TButton;
    procedure NewSite1Click(Sender: TObject);
    procedure CorelButton3Click(Sender: TObject);
    procedure CorelButton1Click(Sender: TObject);
    procedure CorelButton2Click(Sender: TObject);
    procedure TreeView1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
procedure WriteToIniFile(Node : TTreeNode ; Num : integer);
    function GetNodePath(Node : TTreeNode) : string;
    function GetNumNodes : integer;
    procedure ReadAndCreateNodes(num : integer);
    function CreateNode(Nodename : string) : TTreeNode;
    function FindNode(ParentNode : TTreeNode ; NodeName : string) : TTreeNode;
    function GetFirstNodeName(var NodeName : string) : string;
    procedure EnableEdits;
    procedure DisableEdits;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses Unit2;

{$R *.DFM}

function TForm1.GetNodePath(Node: TTreeNode): string;
// get the full path of a node, and return as string like 'node1~node2~node3'
var
  tempNode : TTreeNode;
begin
  tempNode := Node;
  result := '';
  with tempNode as TTreeNode do
    begin
      while tempNode.AbsoluteIndex <> 0 do
        begin
          result := '~' + tempnode.text + result;
          tempNode := tempNode.Parent;
        end;
      result := tempnode.text + result;
    end;
end;

procedure TForm1.WriteToIniFile(Node: TTreeNode; Num : integer);
// write each nodes path and data to the inifile
var
 IniFile : TIniFile;
begin
  with Node as TTreeNode do
    begin
      try
        IniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'Accounts.ini');
        IniFile.WriteString('Nodes', 'Node' + IntToStr(num),GetNodePath(Node));
        IniFile.WriteInteger('Nodes', 'NumNodes',Num);
        IniFile.WriteInteger('Node' + IntToStr(num),'Style',ord(PNodeInfo(Data)^.NodeStyle));
        IniFile.WriteString('Node' + IntToStr(num),'Option1',PNodeInfo(Data)^.Option1);
        IniFile.WriteString('Node' + IntToStr(num),'Option2',PNodeInfo(Data)^.Option2);
        IniFile.WriteString('Node' + IntToStr(num),'Option3',PNodeInfo(Data)^.Option3);
        IniFile.WriteString('Node' + IntToStr(num),'Option4',PNodeInfo(Data)^.Option4);
        IniFile.WriteString('Node' + IntToStr(num),'Option5',PNodeInfo(Data)^.Option5);
        IniFile.WriteString('Node' + IntToStr(num),'Option6',PNodeInfo(Data)^.Option6);
      finally
        IniFile.free;
      end;
    end;
end;

function TForm1.GetNumNodes: integer;
var
  IniFile : TIniFile;
begin
  result := 0;
  try
    IniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'Accounts.ini');
    result := IniFile.ReadInteger('Nodes', 'NumNodes', 0);
  finally
    IniFile.free;
  end;
end;

procedure TForm1.ReadAndCreateNodes(num: integer);
// read each entry from the inifile and create each node
var
  IniFile : TIniFile;
  NodeName : string;
  NodeOption : string;
  Node : TTreeNode;
begin
  try
    IniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'Accounts.ini');
    NodeName := IniFile.ReadString('Nodes', 'Node' + IntToStr(num), '');
    Node := CreateNode(NodeName);
    PNodeInfo(Node.data)^.NodeStyle := TNodeStyle(IniFile.ReadInteger('Node' + IntToStr(num),'Style', 0));
    PNodeInfo(Node.data)^.Option1 := IniFile.ReadString('Node' + IntToStr(num),'Option1', '');
    PNodeInfo(Node.data)^.Option2 := IniFile.ReadString('Node' + IntToStr(num),'Option2', '');
    PNodeInfo(Node.data)^.Option3 := IniFile.ReadString('Node' + IntToStr(num),'Option3', '');
    PNodeInfo(Node.data)^.Option4 := IniFile.ReadString('Node' + IntToStr(num),'Option4', '');
    PNodeInfo(Node.data)^.Option5 := IniFile.ReadString('Node' + IntToStr(num),'Option5', '');
    PNodeInfo(Node.data)^.Option6 := IniFile.ReadString('Node' + IntToStr(num),'Option6', '');
      except
  end;
end;

function TForm1.CreateNode(Nodename: string): TTreeNode;
// create the nodes at the proper place, based on it's fullpath in NodeName
var
  temp : string;
  NodeText : string;
  FoundNode : TTreeNode;
  ParentNode : TTreeNode;
  NodeInfo : PNodeInfo;
begin
  temp := NodeName;
  ParentNode := nil;
  while temp <> '' do
    begin
      NodeText := GetFirstNodeName(temp);
      FoundNode := FindNode(ParentNode,NodeText);
      if FoundNode = nil then
        begin
          New(NodeInfo);
          ParentNode := TreeView1.items.AddChildObject(ParentNode,NodeText,NodeInfo);
        end
      else
        ParentNode := FoundNode;
    end;
  result := ParentNode;
end;

function TForm1.FindNode(ParentNode : TTreeNode ; NodeName: string): TTreeNode;
// find a particular node by name, checking the parent is the correct one
// this allows sub nodes to have the same name (different parents)
var
  t : integer;
begin
  for t := 0 to Treeview1.items.count -1 do
    if ((TTreenode(treeview1.items[t]).text = NodeName) and
     (TTreenode(treeview1.items[t]).HasAsParent(ParentNode))) then
      begin
        result := treeview1.items[t];
        exit;
      end;
  result := nil;
end;

function TForm1.GetFirstNodeName(var NodeName: string): string;
// returns the first node name from the fullpath, and also removes
// the first node name from the full path
// eg enter with Node1~Node2~Node3, and exit with result := Node1 and
// NodeName := Node2~Node3
begin
  if pos('~',NodeName) > 0 then
    begin
      result := Copy(NodeName,1,Pos('~',NodeName)-1);
      NodeName := Copy(NodeName,Pos('~',NodeName) + 1, length(NodeName) - Pos('~',NodeName));
    end
  else
    begin
      result := NodeName;
      NodeName := '';
    end;
end;


procedure TForm1.NewSite1Click(Sender: TObject);
Begin
EnableEdits;
CoolEdit1.SetFocus;
CorelButton2.SendToBack;
end;

procedure TForm1.EnableEdits;
Begin
Form1.CoolEdit1.ReadOnly := False;
Form1.CoolEdit2.ReadOnly := False;
Form1.CoolEdit3.ReadOnly := False;
Form1.CoolEdit4.ReadOnly := False;
Form1.CoolEdit5.ReadOnly := False;
Form1.CoolMemo1.ReadOnly := False;
End;

procedure TForm1.DisableEdits;
Begin
Form1.CoolEdit1.ReadOnly := True;
Form1.CoolEdit2.ReadOnly := True;
Form1.CoolEdit3.ReadOnly := True;
Form1.CoolEdit4.ReadOnly := True;
Form1.CoolEdit5.ReadOnly := True;
Form1.CoolMemo1.ReadOnly := True;
End;

procedure TForm1.CorelButton3Click(Sender: TObject);
var
  t : integer;
  aNode : TTreeNode;
  ParentNode : TTreeNode;
  NodeInfo : PNodeInfo;
begin
  if CoolEdit1.Text <> '' then {There's a site label}
    begin
      New(NodeInfo);
      NodeInfo^.NodeStyle := nsSite; {We're Creating a site, not a folder}
      NodeInfo^.Option1 := CoolEdit1.Text; {Site Label}
      NodeInfo^.Option2 := CoolEdit2.Text; {Remote Host}
      NodeInfo^.Option3 := CoolEdit3.Text; {Username}
      NodeInfo^.Option4 := CoolEdit4.Text; {Password}
      NodeInfo^.Option5 := CoolEdit5.Text; {Port}
      NodeInfo^.Option6 := CoolMemo1.Text; {Notes}
if Treeview1.Selected = nil then {There is no selection}
Begin
Treeview1.Items.AddChildObjectFirst(nil, CoolEdit1.Text, NodeInfo);
end
else
if PNodeInfo(Treeview1.Selected.Data)^.NodeStyle = nsSite then {Don't allow sites to have childeren}
Begin
Treeview1.Items.AddChildObjectFirst(Treeview1.Selected.Parent, CoolEdit1.Text, NodeInfo);
End


else {We can make a site a child of a folder}
Begin
ParentNode := Treeview1.Selected;
//if PNodeInfo(ParentNode.Data)^.NodeStyle = nsSite then
Treeview1.Items.AddChildObjectFirst(Treeview1.Selected, CoolEdit1.Text, NodeInfo);
end;
CorelButton3.SendToBack;
DisableEdits;
for t := 0 to Treeview1.items.Count -1 do
WriteToIniFile(TreeView1.items[t], t);
end;
end;

procedure TForm1.CorelButton1Click(Sender: TObject);
begin
Close; {Hide Form in Final}
end;

procedure TForm1.CorelButton2Click(Sender: TObject);
begin
EnableEdits;
CoolEdit1.SetFocus;
CorelButton2.SendToBack;
end;

procedure TForm1.TreeView1Click(Sender: TObject);
begin
  with treeview1 do
    begin
      if selected = nil then exit;
      case PNodeInfo(selected.data)^.NodeStyle of
        nsFolder:
          begin
//            edit1.text := PNodeInfo(Selected.data)^.option1;
//            edit2.text := '';
          end;
        nsSite:
          begin
            CoolEdit1.Text := PNodeInfo(Selected.data)^.Option1;
            CoolEdit2.Text := PNodeInfo(Selected.data)^.Option2;
            CoolEdit3.Text := PNodeInfo(Selected.data)^.Option3;
            CoolEdit4.Text := PNodeInfo(Selected.data)^.Option4;
            CoolEdit5.Text := PNodeInfo(Selected.data)^.Option5;
            CoolMemo1.Text := PNodeInfo(Selected.data)^.Option6;
          end;
       end;
    end;
end;

procedure TForm1.FormCreate(Sender: TObject);
// read all nodes from the inifile
var
  NumNodes : integer;
  t : integer;
begin
  NumNodes := GetNumNodes;
  for t := 0 to NumNodes do
    ReadAndCreateNodes(t);

end;




procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Show;
end;

end.


I get an error when saving a second site that doesn't have a parent (haven't implemented the adding of folders yet).

Here is the line:
WriteToIniFile(TreeView1.items[t], t);

No extra info is given and I can't figure out why this is happening.

0
Psylord
Asked:
Psylord
  • 5
  • 4
1 Solution
 
mullet_attackCommented:
The error is in "getnodepath"

function TForm1.GetNodePath(Node: TTreeNode): string;
// get the full path of a node, and return as string like 'node1~node2~node3'
var
  tempNode : TTreeNode;
begin
  tempNode := Node;
  result := '';
  with tempNode as TTreeNode do
    begin
      while (tempNode.AbsoluteIndex <> 0) do
        begin
          result := '~' + tempnode.text + result;
          tempNode := tempNode.Parent;
        end;
      result := tempnode.text + result;
    end;
end;

the line while tempnode.absoluteindex <> 0 assumes that there is only one root node, and therefore all aother nodes have a parent. In your case you have two sites without folders, and therefore two 'rootnodes' without parents. In the case absoluteindex = 1 (as for the second site) the line 'tempNode := tempNode.Parent' sets tempnode to nil, and the next loop of tempnode.absoluteindex explodes.

here's the fix

function TForm1.GetNodePath(Node: TTreeNode): string;
// get the full path of a node, and return as string like 'node1~node2~node3'
var
  tempNode : TTreeNode;
begin
  tempNode := Node;
  result := '';
  with tempNode as TTreeNode do
    begin
      while tempNode.Parent <> nil do
        begin
          result := '~' + tempnode.text + result;
          tempNode := tempNode.Parent;
        end;
      result := tempnode.text + result;
    end;
end;

I check to see that the node has a parent, rather than checking the absolute index.
0
 
PsylordAuthor Commented:
Adjusted points from 0 to 5
0
 
PsylordAuthor Commented:
That worked. Great.

One more little thing...
When folders are added, they're being assigned OPtion values (option1, option2 etc.).

Any suggestions about how I should go about fixing that? Something in this procdure should be changed I think:

procedure TForm2.WriteToIniFile(Node: TTreeNode; Num : integer);
var
 IniFile : TIniFile;
begin
   with Node as TTreeNode do
begin
   try
    IniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'Accounts.ini');
    IniFile.WriteString('Nodes', 'Node' + IntToStr(num),GetNodePath(Node));
    IniFile.WriteInteger('Nodes', 'NumNodes',Num);
    IniFile.WriteInteger('Node' + IntToStr(num),'Style',ord(PNodeInfo(Data)^.NodeStyle));
    IniFile.WriteString('Node' + IntToStr(num),'Option1',PNodeInfo(Data)^.Option1);
    IniFile.WriteString('Node' + IntToStr(num),'Option2',PNodeInfo(Data)^.Option2);
    IniFile.WriteString('Node' + IntToStr(num),'Option3',PNodeInfo(Data)^.Option3);
    IniFile.WriteString('Node' + IntToStr(num),'Option4',PNodeInfo(Data)^.Option4);
    IniFile.WriteString('Node' + IntToStr(num),'Option5',PNodeInfo(Data)^.Option5);
    IniFile.WriteString('Node' + IntToStr(num),'Option6',PNodeInfo(Data)^.Option6);
   finally
    IniFile.free;
end;
end;
end;


Thanks!
-Psylord
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
mullet_attackCommented:
looks like option data is not relevant for a folder. You can use the NodeStyle element to determine what gets written to / read from the ini. You could use an 'if Nodestyle = nsSite then WriteStuff', but I would use a case statement, even if there is nothing to write for a folder cos I may want to save some folder options later.

Here's my modified write procedure:

procedure TForm1.WriteToIniFile(Node: TTreeNode; Num : integer);
// write each nodes path and data to the inifile
var
 IniFile : TIniFile;
begin
  with Node as TTreeNode do
    begin
      try
        IniFile := TIniFile.create('MyNodes.ini');
        IniFile.WriteString('Nodes', 'Node' + IntToStr(num),GetNodePath(Node));
        IniFile.WriteInteger('Nodes', 'NumNodes',Num);
        case PNodeInfo(Data)^.NodeStyle of
          nsSite:
            begin
              IniFile.WriteInteger('Node' + IntToStr(num),'Style',ord(PNodeInfo(Data)^.NodeStyle));
              IniFile.WriteString('Node' + IntToStr(num),'Option1',PNodeInfo(Data)^.Option1);
              IniFile.WriteString('Node' + IntToStr(num),'Option2',PNodeInfo(Data)^.Option2);
            end;
          nsFolder:; //whatever get written for a folder
        end;
      finally
        IniFile.free;
      end;
    end;
end;

and of course we need to change this as well :

procedure TForm1.ReadAndCreateNodes(num: integer);
// read each entry from the inifile and create each node
var
  IniFile : TIniFile;
  NodeName : string;
  NodeOption : string;
  Node : TTreeNode;
begin
  try
    IniFile := TIniFile.create('MyNodes.ini');
    NodeName := IniFile.ReadString('Nodes', 'Node' + IntToStr(num), '');
    Node := CreateNode(NodeName);
    PNodeInfo(Node.data)^.NodeStyle := TNodeStyle(IniFile.ReadInteger('Node' + IntToStr(num),'Style', 0));
    case PNodeInfo(Node.data)^.NodeStyle of
      nsSite:
        begin
          PNodeInfo(Node.data)^.Option1 := IniFile.ReadString('Node' + IntToStr(num),'Option1', '');
          PNodeInfo(Node.data)^.Option2 := IniFile.ReadString('Node' + IntToStr(num),'Option2', '');
        end;
      nsFolder:; // whatever gets read for a folder
    end;      
  finally
    IniFile.free;
  end;
end;


0
 
mullet_attackCommented:
Oops, bug ! the writing of NodeStyle needs to be outside the case statement...

procedure TForm1.WriteToIniFile(Node: TTreeNode; Num : integer);
// write each nodes path and data to the inifile
var
 IniFile : TIniFile;
begin
  with Node as TTreeNode do
    begin
      try
        IniFile := TIniFile.create('MyNodes.ini');
        IniFile.WriteString('Nodes', 'Node' + IntToStr(num),GetNodePath(Node));
        IniFile.WriteInteger('Nodes', 'NumNodes',Num);
        IniFile.WriteInteger('Node' + IntToStr(num),'Style',ord(PNodeInfo(Data)^.NodeStyle));
        case PNodeInfo(Data)^.NodeStyle of
          nsSite:
            begin
              IniFile.WriteString('Node' + IntToStr(num),'Option1',PNodeInfo(Data)^.Option1);
              IniFile.WriteString('Node' + IntToStr(num),'Option2',PNodeInfo(Data)^.Option2);
            end;
          nsFolder:; //whatever get written for a folder
        end;
      finally
        IniFile.free;
      end;
    end;
end;
0
 
PsylordAuthor Commented:
That works great... Thanks!

Can you tolerate me a little more?
Hope you don't find this to be "doing all of the work".

For Deleting sites/folders:
Currently I have this code in the click event of my "Delete" button:

var
  ParentNode : TTreeNode;
  NodeInfo : PNodeInfo;
  t : integer;
  aNode : TTreeNode;
  IniFile : TIniFile;
begin
  with Treeview1 do
    begin
      if Selected = nil then exit;
      case PNodeInfo(selected.data)^.NodeStyle of
        nsFolder: {We want to delete a folder!}
          begin
           if MessageBox(Form2.Handle,'Are you sure you want to delete this folder? All subitems will also be deleted.','Confirm',mb_YesNo or mb_IconQuestion or mb_DefButton1) <> IDNO then
           Begin
           if Treeview1.Items.Count = 1 then {This is the only site - Bugfix}
           Begin
           Form2.TreeView1.Selected.Delete;
           IniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'Accounts.ini');
           IniFile.EraseSection('Nodes');
           IniFile.EraseSection('Node0');
           end
           else
           Form2.TreeView1.Selected.Delete;
           {Perform the update}
           for t := 0 to Form2.Treeview1.items.Count -1 do
           Form2.WriteToIniFile(Form2.TreeView1.items[t], t);
           end
          end;
        nsSite: {We want to delete a site!}
        begin
          if MessageBox(Form2.Handle,'Are you sure you want to delete this site?','Confirm',mb_YesNo or mb_IconQuestion or mb_DefButton1) <> IDNO then
          Begin
          if Treeview1.Items.Count = 1 then {This is the only site - Bugfix}
          Begin
          Form2.TreeView1.Selected.Delete;
          IniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'Accounts.ini');
          IniFile.EraseSection('Nodes');
          IniFile.EraseSection('Node0');
          end
          else
           Form2.TreeView1.Selected.Delete;
          {Perform the update}
           for t := 0 to Form2.Treeview1.items.Count -1 do
           Form2.WriteToIniFile(Form2.TreeView1.items[t], t);
           end
          end;
       end;
    end;

This works out... But not as well as I'd like it to.
Although it "deletes" sites/folders, it doesn't actually cleanup after it's done.. meaning there is orphan data left in the ini.

I realize that this only matters after a person has a smaller number of sites than they have before, but I'd still like it to be tidy (removing orphan node sections).

I have a feeling the above code might not be relevant, I really don't wanna keep that code. Anyways, instead the following procedure might need to be modified instead? Here's updated code from your last modification:

procedure TForm2.WriteToIniFile(Node: TTreeNode; Num : integer);
// write each nodes path and data to the inifile
var
  IniFile : TIniFile;
begin
   with Node as TTreeNode do
begin
   try
   IniFile := TIniFile.create('MyNodes.ini');
   IniFile.WriteString('Nodes', 'Node' + IntToStr(num),GetNodePath(Node));
   IniFile.WriteInteger('Nodes', 'NumNodes',Num);
   IniFile.WriteInteger('Node' + IntToStr(num),'Style',ord(PNodeInfo(Data)^.NodeStyle));
   case PNodeInfo(Data)^.NodeStyle of
   nsSite:
begin
   IniFile.WriteString('Node' + IntToStr(num),'Option1',PNodeInfo(Data)^.Option1);
   IniFile.WriteString('Node' + IntToStr(num),'Option2',PNodeInfo(Data)^.Option2);
   IniFile.WriteString('Node' + IntToStr(num),'Option3',PNodeInfo(Data)^.Option3);
   IniFile.WriteString('Node' + IntToStr(num),'Option4',PNodeInfo(Data)^.Option4);
   IniFile.WriteString('Node' + IntToStr(num),'Option5',PNodeInfo(Data)^.Option5);
   IniFile.WriteString('Node' + IntToStr(num),'Option6',PNodeInfo(Data)^.Option6);
end;
   nsFolder:; //whatever get written for a folder
end;
   finally
   IniFile.free;
end;
end;
end;

I have three more questions after this (relativly simple - Editing, renaming folders, adding treeview items to a popup menu and reading values based on the menu item text).

Hope you're not getting irritated with me yet. :) Hopefully being the main coder behind the best Account Manager on Earth is some sort of motivation. :)~

-Psylord
0
 
PsylordAuthor Commented:
Just like to add something...

All of this work being done won't get me a dime. My FTP Client will be freeware.
0
 
mullet_attackCommented:
No, not irritated :-)

I'm happy to help, but this forum is not really the place for this ongoing discussion. Seeing as I answered your previous Q, and you gave points, I guess we have satisfied the requirements of E-E, and therefore we should take this discussion elsewhere. (unless somebody objects !)

My e-mail is mbarber@ozemail.com.au
0
 
PsylordAuthor Commented:
Here's another 5 points for you... Woohooo!

-Psylord
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 5
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now