Solved

Records

Posted on 2001-06-28
45
249 Views
Last Modified: 2010-04-06
I am putting a small program together using Delphi 2 to use as a small address book.

I am using records to keep the information on each person together with their telephone number, address, etc and then using Write(File, Rec) to save the information.

i am using a treeview to hold letters from A to Z and i insert a record under each node letter and use the imageindex of the sibling node to hold the record number.

eg:

A
B
.
J
..Jones (imageindex = 2)
.
S
..Smith (imageindex = 1)
.
.
Z

My problem is how can i save the contents of the treeview along with the information i enter as one file so it can be restored when i reload it at a later date.

Keep in mind i have included the ability to add and delete records to the program.

Anybody got any good ideas on how to do this?
0
Comment
Question by:mi6agent
  • 22
  • 10
  • 6
  • +3
45 Comments
 
LVL 22

Expert Comment

by:mnasman
Comment Utility
Hello

  Why u you don't use the database to save and restore ur data?, it's will be better than using files,

MOhammed
0
 
LVL 6

Expert Comment

by:Jaymol
Comment Utility
Hi.

Can't you just recreate the treeview from the data stored in the records file?

John.
0
 

Author Comment

by:mi6agent
Comment Utility
mnasman, i don't want to start learning the database side of delphi 2 as it is only a small program for my own use and i have no examples to learn from.

jaymol, i was thinking along the same lines but i am unable to figure out how best to do it that way.
0
 
LVL 6

Expert Comment

by:Jaymol
Comment Utility
I would suggest that during the read process, simply pass the information to the procedure that creates the treeview when data is entered.

Do you store the record number (imageindex) in the data file?

John.
0
 

Author Comment

by:mi6agent
Comment Utility
jaymol, i don't store the imageindex in the data file.  at the moment i have the treeview setup so i can add/delete nodes and siblings and i have it set up so i can add/delete/edit records BUT i am unable to put the both ideas together and am not able to figure out a good way to do it.

i am willing to increase the points for help in solving this problem.
0
 
LVL 6

Expert Comment

by:Jaymol
Comment Utility
Is there any way I could see your problem?  I can't quite picture exactly what it is.

John.
0
 

Author Comment

by:mi6agent
Comment Utility
Jaymol,

What i am doing is adapting a program i found here:

http://www.delphicorner.f9.co.uk/tips/apps/apps6.htm

What i am doing is:

Added a treeview to the form and code to enter/edit/delete main titles (nodes: such as A..B..C etc) and also add/edit/delete siblings (the actual record title such as Smith, jones etc)  When each sibling is added i record a number in the imageindex of the sibling as i want this to point to the actual reord in the file.

My problem is that i am finding it hard to be able to link the info together in such as way so that i can have only one file instead of saving the records and treeview as different files.

can't figure out what the best way is to link them.

 
0
 
LVL 6

Expert Comment

by:Jaymol
Comment Utility
You need to save the record number with the data.  Then, when you import the records again, you have enough information to rebuild the treeview.  Is that right?

John.
0
 
LVL 6

Expert Comment

by:Jaymol
Comment Utility
Change this....

    type
      TAddressRec = record
        First : String[20];
        Last  : String[20];
        Add1  : String[40];
        Add2  : String[40];
        City  : String[30];
        ST    : String[2];
        Zip   : String[10];
        Phone : String[20];
        Fax   : String[20];
      end;

to this....

    type
      TAddressRec = record
        RecNum : Integer;
        First  : String[20];
        Last   : String[20];
        Add1   : String[40];
        Add2   : String[40];
        City   : String[30];
        ST     : String[2];
        Zip    : String[10];
        Phone  : String[20];
        Fax    : String[20];
      end;

and make sure that you put the same number in the RecNum field as you put in the imageindex.

John.
0
 

Author Comment

by:mi6agent
Comment Utility
Problem is that the main nodes are not saved so i am unable to rebuild the treeview - if i saved the node information in the reord would that not be a waste of space and cause problems?

what about when i delete a record?  would that not cause things to go wierd with the numbers since i am using those to call up the record when someone click on the sibling?

example, using the program if i create 4 nodes (A, J, S and W) and under A create "ash" it will have the imageindex of 1 since it is the first record created.

then if i enter a sibling (Jones) under the J node it will be 2 and then 3 would be assigned to Smith when i create it under the S node and 4 for Williams under the W.  If i then delete Smith would that not cause problems?
0
 
LVL 3

Expert Comment

by:MarcG
Comment Utility
hm, I remember in Pascal it was possible to create a file of record and then when reading the file you could read out the records. Don't know D2 but I think you could use this too to store your data.
then when reading the data you can just create the whole treeview.
first create the nodes with the letters a to z.
then take the first letter of the name you want to insert and compare it with the nodes text to find the right letter(node) something like ...

function TForm1.FindNode(AChar : String; MyNode : TTreeNode) : TTreeNode;
var TempNode : TTreeNode;
begin
  Result := NIL;
  if MyNode.text = AChar then
  begin
    Result := MyNode;
    exit;
  end
  else
  begin
    tempNode := MyNode.GetNext;
    if not (tempNode = NIL) then
      Result := FindNode(AChar, tempNode)
    else
    begin
      tempNode := MyNode.GetNextSibling;
      if not (tempNode = NIL) then
        Result := FindNode(AChar, tempNode);
    end;
  end;
end;

this function returns the node that has the right letter.
then you can add a new child to that letter with the contents off the record.

here's another example

you have already a treeview containing 26 nodes that have NIL as parent and texts from a to z. You read in a record let'S say it is the one with Jones.
You take the first letter J...
var MyParentNode : TTreeNode;
  MyParentNode := FindNode('J', TreeView1.Items.GetFirstNode);
  TreeView1.Items.AddChild(MyParentNode, 'Jones');

Do this for all your records ...

To save the data when changed just go through all you nodes and write the data to a file.
0
 
LVL 3

Expert Comment

by:MarcG
Comment Utility
oops, missed out some comments while i was writing mine
0
 
LVL 3

Expert Comment

by:MarcG
Comment Utility
to you last comment... just store a counter with your data that will count the number f records entered and if you delete on (for example you entered 3 and then delete the third) the next you enter will be numbered 4 eventhough no 3 exists anymore, as far as i can see this won't cause any problems since you are not really using this number to build you treeview. something like this is also done in databases by the way.
0
 

Author Comment

by:mi6agent
Comment Utility
Thanks MarcG but that would mean i am stuck with the A-Z nodes and not able to expand the program at a later date if i need to.

I shall post some points for your help though.
0
 

Author Comment

by:mi6agent
Comment Utility
i'm using the imageindex to perform a seek command to the record - if the numbers are all out then i can see problems when i add 10 records and delete 5 and then click a sibling with the record number 10 and try to seek to it when only 5 records are in the file.
0
 
LVL 6

Expert Comment

by:Jaymol
Comment Utility
I'm sorry, but I don't think I'm entirely understanding the problem.

John.
0
 

Author Comment

by:mi6agent
Comment Utility
with luck this will make it a bit clearer (as mud at least)

Instead of using the buttons to goto the previous, next, first or last number i want to be able to click the sibling and use the imageindex number to seek to the record and display it.

the problem is that, there is only the ablitity to hold upto 2000 records so the imageindex numbers will go from 1 to 2000 for a full file - as long as i do not delete anything because when i do it will then be 2001 which if i try to seek to the record won't exist.

add to that the best way to save the extra info (the node title, the sibling title and the imageindex of the sibling) into the file structure and be able to go through the file and rebuild the treeview and you an see why i am having trouble figuring it out.

0
 

Author Comment

by:mi6agent
Comment Utility
i've increased the points in the hope of getting help on this one as i am stuck on it.
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
could provide a working sample this evening,
but guessing thats too late :-(
0
 

Author Comment

by:mi6agent
Comment Utility
Kretzschmar, if you can i shall award you the points if i don't have an answer by then.

if i do get an answer i shall post the same points for you (in fact i shall increase the points) in another post.
0
 
LVL 3

Expert Comment

by:MarcG
Comment Utility
so you want to have some data with your record that makes it possible to identify it. again like in databases, add a key or id to it and if you click on a node you search your records for the right ID (that you set when creating the node)
by the way, I would suggest using the tag property for your numbering since it is not used for anything else and if you think about later needs it would be the better solution, what if you want to add pictures for example for male or female contacts ? then you would need the image index for it...
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
>Instead of using the buttons to goto the previous, next,
>first or last number i want to be able to click
>the sibling and use the imageindex number to seek to the
>record and display it.

use the onchange-event of your tree for this

>the problem is that, there is only the ablitity to hold upto 2000 records so the imageindex numbers
>will go from 1 to 2000 for a full file - as long as i do not delete anything because when i do it will
>then be 2001 which if i try to seek to the record won't exist.

you could only mark one as deleted in your file,
and at appstartup you could clean up the file, to delete the marked records from the last session

>add
just append to the file and add anode

meikl ;-)


0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:mi6agent
Comment Utility
excellent idea MarcG - duly noted, thanks!
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
>you could only mark one as deleted in your file,
should read
you could mark the record as deleted in your file
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
marcG,
this would cause an already sequential read of the file in every nodechange, to find the record
0
 

Author Comment

by:mi6agent
Comment Utility
meikl, thing is that cleaning up would be the problem since all the records would not be stored in sequence 1...2000.  the file might be 1 ash, 2 williams, 3 jones, etc.
0
 

Author Comment

by:mi6agent
Comment Utility
meikl, the onchange idea marcG gave is what i already use.  when i click the "ash" sibling it gives me the number in the imageindex which i then seek to in the file.  same for the other siblings.
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
>meikl, thing is that cleaning up would be the problem
>since all the records would not be stored in sequence

that doesn't matter, because i meant an attribute in your record in your file, which could be used for marking or a new attribute

following flow i have in mind

appstart
delete marked records
load the tree->remember current positions somewhere in the node (tag,data)
add(appendtofile),modify(replcerecord),delete(markrecord)
end of flow

meikl ;-)
0
 

Author Comment

by:mi6agent
Comment Utility
sounds like a plan meikl - seeing it in working code would be the clinch.
0
 
LVL 3

Expert Comment

by:MarcG
Comment Utility
just a thought about reading and rereading the file ... can't you make an array of record to store the records in memory (depending on how big it is but I think some MB of memory needed is not too much) then you can access them faster, and at the end write them back to disk.
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
as stated above, i can't do it before this evening,
because here at work i've no delphi installed
(working with oracle forms since months for a project :-( )

and i will not block other experts to provide solutions, which are helping you, sothat i will provide
a working sample if this q is unsolved. this may take
~8 hours from now.

meikl ;-)
0
 

Author Comment

by:mi6agent
Comment Utility
MarcG, i looked at the idea of using an array instead of the current record method but it would put some restrictions on the program such as the amount of memory it used when it is run and would limit memory for other programs running.


Meikl, seems you have the answer.....help!! :)
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
yes, i guess, but got not the time yesterday to code it(family-action), sorry,
even ex-ex wasn't available to me since yesterday afternoon until this morning, didn't know why

well everyone can pick-up the idea and code a sample instead of myself, because i can't do it before late afternoon today (family is informed :-)
0
 

Author Comment

by:mi6agent
Comment Utility
Well Meikl, looks that no one is able to help on this one.  i shall keep the question open and hope that you can get some spare time soon to help me on this one.  If you can help me solve my problem i shall increase the points to 400 plus an A grade.
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
well, you do not need inceasing points for me,
as my family is informed i get two hours this afternoon,
to implement this idea, guessing i need only one hour,
because i have some codefragment ready its just to complete so that it fits your needs

i provide it today, even if some other expert solved this q

meikl ;-)
0
 

Author Comment

by:mi6agent
Comment Utility
thanks meikl, i look forward to your help and as for the extra points i offer - i think your help is worth it and it is my way of saying thanks.
0
 
LVL 1

Expert Comment

by:alx512
Comment Utility
Hi! If you can't use the database to store data,
you can work with compound storages.
0
 

Author Comment

by:mi6agent
Comment Utility
Hi alx512, not sure i understand what you mean about "compound storage".  
0
 
LVL 27

Expert Comment

by:kretzschmar
Comment Utility
well, here it comes, maybe not bugfree in all cases, maybe something could be done more elegant

unit r_file_tree_u;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, ExtCtrls;

Type TheRec = Record  //a record
                LastName  : String[100];
                FirstName : String[100];
                //more attributes could stand here
                Deleted  : Boolean;
              end;

type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Panel1: TPanel;
    Edit1: TEdit;
    Edit2: TEdit;
    BInsert: TButton;
    BUpdate: TButton;
    BDelete: TButton;
    procedure FormCreate(Sender: TObject);
    procedure BInsertClick(Sender: TObject);
    procedure BUpdateClick(Sender: TObject);
    procedure BDeleteClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure FormDestroy(Sender: TObject);
  private
    NodeList : TList;      //AList of the rootNodes
    f : file of TheRec;    // file of this record
    rec : TheRec;   {Buffer} // a memorybuffer for this record
    Currentrecord : LongInt; {FileCursor}  //Points to the current record in the file
    RecordCount : LongInt; {RecordCount} //Holds the amount of records in the file
    Procedure PopulateEdits;
    Procedure PopulateRecord;
    Procedure CleanUpFile_at_Back;  //looks at the last record, if this is deleted, and deletes it
    Procedure CleanUpFile;          //Deletes all records, which are marked for deletion
    Procedure InitTree;             //adds the level one nodes
    Procedure LoadTree;             //adds the level two nodes from files
    Procedure GetRecord(ARecNo : LongInt);  //gets a record
    Procedure UpdateRecord;         //Upsate a Record
    Procedure MarkToDeleteRecord;   //Marks a record for delete
    Procedure InsertNode(AName : String; ARecNo : LongInt; DoSelect : Boolean);  //Inserts a Node into the tree
    Procedure AdJustControls(Switch : Boolean);  //Dis-/Enables Buttons
    Procedure InsertRecord;         //Insert a Record
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;


implementation

{$R *.DFM}

//open the file
procedure TForm1.FormCreate(Sender: TObject);
begin
  assignfile(f,'MyFile.Dat');  //we will work with this file
  if not(fileexists('MyFile.Dat')) then  //if this file not exists
    rewrite(f)  //create this file
  else
    reset(f);   //open this file
  CurrentRecord := 0;      //
  RecordCount := fileSize(f);
  NodeList := Tlist.Create;
  LoadTree;
end;

Procedure TForm1.InsertRecord;
begin
  PopulateRecord;
  rec.Deleted := False;
  seek(f,RecordCount);  //goto last record
  write(f,rec);
  inc(RecordCount);
  CurrentRecord := RecordCount;
  InsertNode(rec.LastName,CurrentRecord,True);
end;

procedure TForm1.BInsertClick(Sender: TObject);
begin
  InsertRecord;
end;

procedure TForm1.BUpdateClick(Sender: TObject);
begin
  UpdateRecord;
end;

procedure TForm1.UpdateRecord;
var
  ParentSwap : Boolean;
begin
  if CurrentRecord > 0 then
  begin
    PopulateRecord;
    //must we Move the ChildeNode
    ParentSwap := ((TreeView1.Selected.Text <> '') and (rec.LastName <> '') and
                  (UpCase(TreeView1.Selected.Text[1]) <> UpCase(rec.LastName[1]))) or
                  ((TreeView1.Selected.Text <> '') and (rec.LastName = '')) or
                  ((TreeView1.Selected.Text = '') and (rec.LastName <> ''));

    TreeView1.Selected.Text := rec.LastName; //Update NodeText
    seek(f,CurrentRecord-1);
    write(f,rec);
    If ParentSwap then
    begin
      TreeView1.Selected.Delete;
      InsertNode(rec.LastName,CurrentRecord,True);
    end
    else
      TreeView1.Selected.Text := rec.LastName; //Update NodeText
  end;
end;

Procedure TForm1.MarkToDeleteRecord;
Begin
  rec.deleted := true;
  seek(f,CurrentRecord-1);
  write(f,rec);
  treeview1.selected.Delete;

End;


procedure TForm1.BDeleteClick(Sender: TObject);
begin
  MarkToDeleteRecord;
end;


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  closefile(f);
end;

Procedure TForm1.AdJustControls(Switch : Boolean);
var I : integer;
begin
  BUpdate.Enabled := Switch;
  BDelete.Enabled := Switch;
  If Not(Switch) then
  begin
    edit1.text := '';
    edit2.text := '';
  end;
end;

procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin

  If Node.Level > 0 then
  begin
    GetRecord(Integer(node.Data));
    PopulateEdits;
  End;
  AdjustControls(Node.Level > 0);
end;

Procedure TForm1.GetRecord(ARecNo : LongInt);
begin
  If (ARecNo > 0) and (ARecNo <= RecordCount) then
  begin
    try
      seek(f,ARecNo-1);
      read(f,rec);
      CurrentRecord := ARecNo;
    except
      raise;
    end;
  End
  else
    raise exception.create('GetRecord : Recno Out of Bounds');
end;

procedure TForm1.PopulateEdits;
begin
  edit1.text := rec.FirstName;
  edit2.text := rec.LastName;
  //maybe more attributes
end;

procedure TForm1.PopulateRecord;
Begin
  rec.FirstName := edit1.text;
  rec.LastName := edit2.text;
  //maybe more attributes
End;



Procedure TForm1.CleanUpFile_at_Back;
var
  endloop : Boolean;
begin
  if RecordCount > 0 then
  begin
    endloop := False;
    while (not endloop) and (RecordCount > 0) do
    begin
      getrecord(RecordCount);
      if rec.Deleted then
      begin
        seek(f,RecordCount-1);
        truncate(f);
        dec(RecordCount);
      end
      else endloop := true;
    end;
  end;
end;

Procedure TForm1.CleanUpFile;
var
  tmpCurrentRecord : Integer;
Begin
  If RecordCount > 0 then
  begin
    tmpCurrentRecord := 1;
    CleanUpFile_at_Back;  //be sure the last record isn't deleted
    while tmpCurrentRecord < RecordCount do
    begin
      GetRecord(tmpCurrentRecord);
      if rec.Deleted then
      begin
        GetRecord(RecordCount);  //swap records
        seek(f,tmpCurrentRecord-1);
        write(f,rec);
        seek(f,RecordCount-1);
        truncate(f);         //snip
        dec(RecordCount);
        CleanUpFile_at_Back; //be sure the new last record isn't deleted
      end;
      inc(tmpCurrentRecord);
    end;
  end;
  CloseFile(f);  //force physically write
  reset(f);
  CurrentRecord := 0;  //reinit
  If (filesize(f) = RecordCount) then
    RecordCount := FileSize(f)
  else
    raise exception.Create('CleanUp : Something goes wrong');
end;

procedure TForm1.InitTree;
var
  ANode : TTreeNode;
  I : Integer;
begin
  NodeList.Clear;
  TreeView1.Items.Clear;
  for I := Ord('A') to Ord('Z') do
    NodeList.Add(Pointer(TreeView1.Items.Add(NIL,Chr(I))));
  NodeList.Add(Pointer(TreeView1.Items.Add(NIL,'Others')));
End;

procedure TForm1.InsertNode(AName : String;
                            ARecNo : LongInt;
                            DoSelect : Boolean);
var
  ANode : TTreeNode;
  AIndex : Integer;
Begin
  If (AName <> '') and
     (UpCase(AName[1]) in ['A'..'Z']) then
    AIndex := Ord(UpCase(AName[1]))-Ord('A')
  else
    AIndex := NodeList.Count-1;
  If (AIndex > -1) and (AIndex < NodeList.Count) then   //be sure not to be out of bounds
  Begin
    ANode := TreeView1.Items.AddChild(Nodelist[AIndex],AName); //Create Node
    ANode.Data := Pointer(ARecNo);  //Store recordposition
    If DoSelect Then
    begin
      TreeView1.Selected := ANode;  //Select Node
      ANode.MakeVisible;
    end;
  end
  else
    raise exception.Create('InsertNode: Something is wrong');
end;

procedure TForm1.LoadTree;
Begin
  CleanUpFile;   //Delete all marked physically
  InitTree;
  CurrentRecord := 0;    //load From Beginning
  seek(f,CurrentRecord);
  While not eof(f) do
  begin
    Read(f,rec);
    inc(CurrentRecord);
    InsertNode(rec.LastName,CurrentRecord,False);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  NodeList.Free; //free the list of RootNodes
end;

end.

btw. i also do not know what compound storage is

ask if something unclear or not work as needed(only partially tested)

meikl ;-)
0
 

Author Comment

by:mi6agent
Comment Utility
Meikl you're a genius!

The only question i have is if i enter some information (a record) under A for example, how can i identify the record number to display if i click on the record name in the treeview?

0
 

Author Comment

by:mi6agent
Comment Utility
oops, ignore that last question meikl - i forgot to link in the treeview.onchange procedure you wrote.

one final question though, if i wanted to use different main nodes (instead of A-Z) would it be difficult to change all the code to allow for that?  example, if i wanted the main nodes to be "family", "friends", "co-workers" and a few others like that.


points have been increased for you to 400 meikl
0
 

Author Comment

by:mi6agent
Comment Utility
strange, it would ex-ex would not allow me to increase it to 400 points.  said that the limit was 300.  so once this question is closed i shall post a further 100 points for you to claim.
0
 
LVL 27

Accepted Solution

by:
kretzschmar earned 300 total points
Comment Utility
>would it be difficult to change all the code to allow for that?

not really, basically the inittree and insertnode-methods must be changed,
additional you need an attribute in your record, which holds the typ.

well, the second version, there are also some validations included,
and i have inserted a combobox, where the typ can be selected

unit r_file_tree2_u;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, ExtCtrls;

Type TheRec = Record  //a record
                LastName  : String[100];
                FirstName : String[100];
                //more attributes could stand here
                Typ      : Integer;
                Deleted  : Boolean;
              end;


type
  TForm1 = class(TForm)
    TreeView1: TTreeView;
    Panel1: TPanel;
    Edit1: TEdit;
    Edit2: TEdit;
    BInsert: TButton;
    BUpdate: TButton;
    BDelete: TButton;
    ComboBox1: TComboBox;
    procedure FormCreate(Sender: TObject);
    procedure BInsertClick(Sender: TObject);
    procedure BUpdateClick(Sender: TObject);
    procedure BDeleteClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure FormDestroy(Sender: TObject);
  private
    NodeList : TList;      //AList of the rootNodes
    f : file of TheRec;    // file of this record
    rec : TheRec;   {Buffer} // a memorybuffer for this record
    Currentrecord : LongInt; {FileCursor}  //Points to the current record in the file
    RecordCount : LongInt; {RecordCount} //Holds the amount of records in the file
    Procedure PopulateEdits;
    Procedure PopulateRecord;
    Procedure CleanUpFile_at_Back;  //looks at the last record, if this is deleted, and deletes it
    Procedure CleanUpFile;          //Deletes all records, which are marked for deletion
    Procedure InitTree;             //adds the level one nodes
    Procedure LoadTree;             //adds the level two nodes from files
    Procedure GetRecord(ARecNo : LongInt);  //gets a record
    Procedure UpdateRecord;         //Upsate a Record
    Procedure MarkToDeleteRecord;   //Marks a record for delete
    Procedure InsertNode(AName : String; AType : Integer; ARecNo : LongInt; DoSelect : Boolean);  //Inserts a Node into the tree
    Procedure AdJustControls(Switch : Boolean);  //Dis-/Enables Buttons
    Procedure InsertRecord;         //Insert a Record
  public
    { Public-Deklarationen }
  end;

//Define your types
Type Types =  (typfamily, typfriends, typco_workers, typunknown);
const TypeLabels : Array[typfamily..typunknown] Of String = ('family','friends','co-workers','Unknown');

var
  Form1: TForm1;


implementation

{$R *.DFM}

//open the file
procedure TForm1.FormCreate(Sender: TObject);
var I : Integer;
begin
  assignfile(f,'MyFile2.Dat');  //we will work with this file
  if not(fileexists('MyFile2.Dat')) then  //if this file not exists
    rewrite(f)  //create this file
  else
    reset(f);   //open this file
  CurrentRecord := 0;      //
  RecordCount := fileSize(f);
  for I := Ord(typfamily) to Ord(typunknown) do
    combobox1.Items.add(TypeLabels[Types(i)]);
  NodeList := Tlist.Create;
  LoadTree;
end;

Procedure TForm1.InsertRecord;
begin
  PopulateRecord;
  rec.Deleted := False;
  seek(f,RecordCount);  //goto last record
  write(f,rec);
  inc(RecordCount);
  CurrentRecord := RecordCount;
  InsertNode(rec.LastName,rec.typ,CurrentRecord,True);
end;

procedure TForm1.BInsertClick(Sender: TObject);
begin
  InsertRecord;
end;

procedure TForm1.BUpdateClick(Sender: TObject);
begin
  UpdateRecord;
end;

procedure TForm1.UpdateRecord;
var
  ParentSwap : Boolean;
begin
  if CurrentRecord > 0 then
  begin
    //must we Move the ChildeNode
    ParentSwap := rec.typ <> combobox1.itemindex;
    PopulateRecord;
    seek(f,CurrentRecord-1);
    write(f,rec);
    If ParentSwap then
    begin
      TreeView1.Selected.Delete;
      InsertNode(rec.LastName,rec.typ,CurrentRecord,True);
    end
    else
      TreeView1.Selected.Text := rec.LastName; //Update NodeText
  end;
end;

Procedure TForm1.MarkToDeleteRecord;
Begin
  rec.deleted := true;
  seek(f,CurrentRecord-1);
  write(f,rec);
  treeview1.selected.Delete;
End;


procedure TForm1.BDeleteClick(Sender: TObject);
begin
  MarkToDeleteRecord;
end;


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  closefile(f);
end;

Procedure TForm1.AdJustControls(Switch : Boolean);
var I : integer;
begin
  BUpdate.Enabled := Switch;
  BDelete.Enabled := Switch;
  If Not(Switch) then
  begin
    combobox1.ItemIndex := -1;
    edit1.text := '';
    edit2.text := '';
  end;
end;

procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin

  If Node.Level > 0 then
  begin
    GetRecord(Integer(node.Data));
    PopulateEdits;
  End;
  AdjustControls(Node.Level > 0);
end;

Procedure TForm1.GetRecord(ARecNo : LongInt);
begin
  If (ARecNo > 0) and (ARecNo <= RecordCount) then
  begin
    try
      seek(f,ARecNo-1);
      read(f,rec);
      CurrentRecord := ARecNo;
    except
      raise;
    end;
  End
  else
    raise exception.create('GetRecord : Recno Out of Bounds');
end;

procedure TForm1.PopulateEdits;
begin
  edit1.text := rec.FirstName;
  edit2.text := rec.LastName;
  //maybe more attributes
  If Types(rec.typ) in [typfamily..typunknown] then
    combobox1.ItemIndex := rec.typ
  else
  begin
    Showmessage('Record have an invalid typ, will be resetted to Unknown!');
    combobox1.ItemIndex := Ord(typunknown);
    rec.typ := Ord(typunknown);
  end;
  //maybe more attributes

end;

procedure TForm1.PopulateRecord;
Begin
  rec.FirstName := edit1.text;
  rec.LastName := edit2.text;
  If Types(combobox1.ItemIndex) in [typfamily..typunknown] then
    rec.typ := combobox1.ItemIndex
  else
    raise exception.create('Typ must be set!');
  //maybe more attributes
End;

Procedure TForm1.CleanUpFile_at_Back;
var
  endloop : Boolean;
begin
  if RecordCount > 0 then
  begin
    endloop := False;
    while (not endloop) and (RecordCount > 0) do
    begin
      getrecord(RecordCount);
      if rec.Deleted then
      begin
        seek(f,RecordCount-1);
        truncate(f);
        dec(RecordCount);
      end
      else endloop := true;
    end;
  end;
end;

Procedure TForm1.CleanUpFile;
var
  tmpCurrentRecord : Integer;
Begin
  If RecordCount > 0 then
  begin
    tmpCurrentRecord := 1;
    CleanUpFile_at_Back;  //be sure the last record isn't deleted
    while tmpCurrentRecord < RecordCount do
    begin
      GetRecord(tmpCurrentRecord);
      if rec.Deleted then
      begin
        GetRecord(RecordCount);  //swap records
        seek(f,tmpCurrentRecord-1);
        write(f,rec);
        seek(f,RecordCount-1);
        truncate(f);         //snip
        dec(RecordCount);
        CleanUpFile_at_Back; //be sure the new last record isn't deleted
      end;
      inc(tmpCurrentRecord);
    end;
  end;
  CloseFile(f);  //force physically write
  reset(f);
  CurrentRecord := 0;  //reinit
  If (filesize(f) = RecordCount) then
    RecordCount := FileSize(f)
  else
    raise exception.Create('CleanUp : Something goes wrong');
end;

procedure TForm1.InitTree;
var
  ANode : TTreeNode;
  I : Integer;
begin
  NodeList.Clear;
  TreeView1.Items.Clear;
  for I := Ord(typfamily) to Ord(typunknown) do
    NodeList.Add(Pointer(TreeView1.Items.Add(NIL,typelabels[Types(i)])));
End;

procedure TForm1.InsertNode(AName : String;
                            AType : Integer;
                            ARecNo : LongInt;
                            DoSelect : Boolean);
var
  ANode : TTreeNode;
  AIndex : Integer;
Begin
  If Types(Atype) in [typfamily..typunknown] then
    AIndex := AType
  else
    AIndex := Ord(typunknown);
  If (AIndex > -1) and (AIndex < NodeList.Count) then   //be sure not to be out of bounds
  Begin
    ANode := TreeView1.Items.AddChild(Nodelist[AIndex],AName); //Create Node
    ANode.Data := Pointer(ARecNo);  //Store recordposition
    If DoSelect Then
    begin
      TreeView1.Selected := ANode;  //Select Node
      ANode.MakeVisible;
    end;
  end
  else
    raise exception.Create('InsertNode: Something is wrong');
end;

procedure TForm1.LoadTree;
Begin
  CleanUpFile;   //Delete all marked physically
  InitTree;
  CurrentRecord := 0;    //load From Beginning
  seek(f,CurrentRecord);
  While not eof(f) do
  begin
    Read(f,rec);
    inc(CurrentRecord);
    InsertNode(rec.LastName,rec.typ,CurrentRecord,False);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  NodeList.Free; //free the list of RootNodes
end;

end.

meikl ;-)
0
 

Author Comment

by:mi6agent
Comment Utility
Brilliant!!!

The 300 points are yours from this question and i shall also post a further 150 for you as well since you gave so much help and were patient with my problem.

I shall grade both with an A as well.
0
 

Author Comment

by:mi6agent
Comment Utility
shall post the other 150 for you now
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
This video discusses moving either the default database or any database to a new volume.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now