3EIT (MEng) - Music Library Database Project 1994-95.  Paul Griffin.

Files:  modmain.tcl, recproc.tcl, addproc.tcl, askproc.tcl,delproc.tcl.



				OVERVIEW.

The assignment was to produce a windows-driven database handler
to replace and improve upon an existing command line based program
written in C. The aim was to allow anyone with just very basic
computing skills to access the database. The database itself remains in
the same format as before. The handler is written in Tcl scripts
utilising the GUI toolkit Tk, and the pattern matching/search programs
AWK and or GREP.

	To split the work between four people we determined what was
	required from such a program. After some discussion we decided
	on the following:

	1/ Search facility:     either          a/ Specific search
				or              b/ Browse mode

	2/ History log + Printout facility

	3/ Modify Database facility comprising  a/ Modify record
						b/ Add record

My section was section 3. This was required for situations such
as items moving location in the library (modify) or a new item
purchased for the library (add). In addition I decided a delete option
would also be neccessary for lost, stolen or damaged items.



				FUNCTIONALITY.

Section 3 consisted of three operations,
		1/ Add new record to database.  
		2/ Modify existing record.  
		3/ Delete existing record.  

A search facility will be present in the final package, so modify and
delete need only act on the 'current' record, i.e.  the record
last searched for/selected.  With a little thought it becomes clear
that a modify operation can (only) be achieved by deleting the old
record (the one to be modified) and adding the new record (the modified
version). A separate modify function is not, therefore, required but is
a combination of the other two operations.

All three of the operations required the record to be displayed, add
and modify required the display to be editable. A function was
required, therefore, to set up editable entry boxes for each field.
This function naturally became the front end of the section and was
made adaptable so that when add, modify or delete were called the
correct front end was created.

Whilst writing and testing the script a dummy test-harness was
required that would emulate the interaction with the final package,
which was deliberately minimal.

After this initial analysis it can be seen that four functions
were required:
		1/ Test harness.  
		2/ Adaptable Front-End. 
 		3/ Add to file script.  
		4/ Delete from file script.



		REALISATION (PROGRAM MANUAL)


		1/ TEST HARNESS 
		FILE: modmain.tcl

This is a very simple script that sets up a dummy frame and a menubar
with the options 'quit' and 'file'. Quit simply destroys the widget,
File has the three options Add, Delete or Modify.  Each of these
options calls the front-end creator, RecordIn, passing it as an
argument the action to be carried out. This function also sets up the
global variables that will be available from the rest of the package
'field'; the list of fields, and 'current(FIELD)' the array holding the
current record. The 'current' array is filled with test data on
creation.


		2/ ADAPTABLE FRONT END 
		FILE:recproc.tcl
		PROC:RecordIn{action}

This procedure sets up labelled entry boxes, in the case of action=MOD
or DEL, the entry boxes are filled with current fields, in the case of
action=ADD (add a new record) they are left empty thereby setting all
current(field) to null. Below the entries two button s are created;
Quit, which is common to all actions, and an 'action' button whose
action changes depending on the action req uired.  A variable message
box also appears with a simple instruction to the user relevant to the
action called for.

This all occurs in a new toplevel widget. This made fitting this
section into the rest of the program much simpler, simply swapping the
toplevel .io widget for a frame in the final package.  The only tricky
part of this procedure is creating a Modify operation from the delete
and add operations. This was achieved by creating a copy of the record
(oldrecord) before it is modified.

This loop had to be placed at the top of the procedure as it must be
evaluated at the time of front end creation. 'Oldrecord' can then be
deleted and current record added, when the button is pressed. A second
copy of the loop was required in the button command (after the delete)
to recreate a copy of the current record (->oldrecord) because if a
further call of the modify button was made the procedure would try to
redelete 'oldrecord' which clearly no longer exists in the file.  The
record can therefore be modified repeatedly without the need to exit
then enter to recreate the front end, thereby evaluating the loop, each time.


		3/ADD TO FILE SCRIPT 
		FILE:addproc.tcl 
		PROC:AddToFile

To add the current file to the database the correct fields must be
joined together as a string and inserted at the correct point in the
database.  All entries in the database are stored below the composer
name (*COMPOSER) in sections.

Problems arise when adding a record with a composer name that does not
yet exist in the file, but because all the composer sections are all in
alphabetical order it is possible to determine whether a composer is
absent from the file without searching all the way through. If the
composer does not exist a new section must be created, if the composer
does exist the entry can be simply tagged on to the bottom of that
composers section.

From this it can be seen that irrespective of whether the composer
exists or not, the insertion point must be just before the start of the
composer's section whose name is next alphabetically. The bulk of the
work in this procedure is, therefore, to attain this correct access
position within the database and at the same time determine whether the
composer exists. This is made possible through the string compare
command which compares two strings and returns -1 if
lexicographically lower (i.e. alphabetically early), 0 if matched
and 1 if alphabetically late.  

To find a composer name to compare, the file is searched for a '*' in
the first character of each line, the composer name after this star is
then compared with the current composer, this goes on until the result
'1' (late) occurrs. If the result '0' occurrs in the meantime a flag is
set indicating that the composer does exist.  At the end of the loop
the access position is just AFTER the alphabetically higher composer so
it is brought back by the seek command using linelength from the 'gets'
return value. This brings the access position to the correct place.

The string to be added to the file must now be created. If the composer
exists the input string is simply "titl + per1 + per2 etc" all
concantenated with the join character '|'. If the composer does not
exist the input string must be "*comp + newline + titl + per1 + per2
etc". This was achieved by setting up an input LIST with a variable
first element dependant on the cmpexists flag, either "*composer + \n
+title" for cmpexists=1, or "title" for cmpexests=0 and then 'join'ing
this list with the joinchar '|' to create a STRING. The inclusion of
the title in the first element prevented a joinchar appearing between
the composer and title. This approach is also neccessary to deal with
null entries; each record will always have the correct number of fields
and so a field entered as a particular field type will always remain of
that type.

Once the input string has been created and the correct access position
found it should be a simple task to 'puts' the string into the file
however this overwrites the file instead of inserting into the file.
The call to tail stores what is after the insertion point (+$linesread)
in a string 'filetail'. The input string can then be add ed followed by
the rest of the file. The file is then closed, flushing the contents of
the buffer automatically.  

An addition was made to ask the user, in the case of the composer not
existing, whether they wished to continue and create a new section or
return. This is done by creating a new top-level widget with a simple
message showing the early and late composers and offering a choice of
two buttons; create new section or return to the editor. This procedure
is called AskUser and is in the separate script file askproc.tcl.


		4/DELETE FROM FILE SCRIPT.  FILE:delproc.tcl
		PROC:DelRecord{name}

To delete a record from a file the access position is set to just after
the record to be deleted, the rest of the file is stored(tail), the
access position is then brought back to the beginning of the
record(seek) and the rest of the file is pasted back(puts), overwriting
the record and effectively deleting it.

The information required is the position of the end of the record and
the length of the record. This information is gained in a similar way
to the AddToFile procedure, however, now we must check the entries in
the composer section for a match.  It can be assumed that the record to
be deleted exists as it is the current record and will have been
obtained (from a search or browse operation) directly from the datafile
that is being searched.

The procedure firstly finds the composer section in LOOP ONE, similar
to the loop in AddToFile but now it exits as soon as the match is
found.  Each entry under the composer is then taken in turn (LOOP TWO),
split into a list and each element of this list compared with the
corresponding current field (FOREACH LOOP).

If one field does not match the corresponding current field; match is
set to 0, the foreach loop is exitted and loop two picks up another
line/record to be split up and compared.

If all fields match then the foreach loop comes to a natural conclusion
with the variable match=1. This condition breaks loop two (this could
have been achieved by while {match != 1} instead of while{1} ) and
takes the program to the 'chop 'n paste' section.

Within the two loops a count was kept of linesread and of the
linelength of the last line read. Linesread is required in the tail operation to store everything below the line to be deleted. Then linelength is used by the seek operation to bring the access position back to the start of the line, at which point the tail of the file can be 'pasted' back over the line, effectively deleting it.

		STANDALONE OPERATION.

After much work and debugging through the inclusion of puts statements
that create a log of what the program is doing on the cmdtool, the
system worked fine. The areas that gave most trouble were creating the
input string (the final soln appears obvious), and the combination of
delete and add to create a modify operation.

The only time problems occur is when prior to deleting a record the
record is edited; as any slight change in the record will prevent
DelRecord from finding it and so prevent it deleting it. This could be
avoided by disabling the entry boxes on the delete option.  Also when a
record is deleted it still remains the current record even though it no
longer exists in the file. If this was then modified or redeleted the
delete procedure would not be able to find it. The current record
should have been set to null after a deletion. This would have also been a useful source of confirmation for the user; after a delete operation the entry boxes would be empty.

The puts strings have been left in for the standalone version to aid
comprehension of the programs operation.  The sole use of Tcl code for
the searching and comparing (instead of AWK or GREP) was neccessary
because it was done to find the correct access position not just
retrieve information. This does lead to quite a slow operation but I
feel that the way the control loops have been designed this is the
quickest way to find the access position. The speed of search is
greatly increased in the final package with the exclusion of the puts
statements.

Unfortunately (due to the pressures of time) the entry boxes do not
have the necessary bindings to facilitate presses of the return/tab key
to cycle the cursor round each box. This is not a complicated procedure
and could easily be implemented at a later date.

		INTEGRATION WITH FINAL PACKAGE.

To integrate my section with the rest of the package was very straight
forward. The front end was always packed into a toplevel widget .io so
this was simply replaced with a frame of the same name in the package.
A menubutton was created with exactly the same callback commands as in
modmain - the test harness. A few syntax and order inconsistencies were
ironed out involving the two globals current, the psuedo array, and
fields, the list of fields for the array, and the path for the
datafile.
A few aesthetic changes eg padding and expanding options were altered
so that the front end fitted into the fixed frame nicely, and the puts
debugging statements were all removed. After this, the package was
tested and proved to work perfectly (eventually!).


		USER MANUAL.

The standalone program runs from modmain.tcl an executable file, a copy of the records file is required in the home directory.
On start-up the dummy frame appears with a menubar offering File or Quit.
Quit will completely quit the entire program. File offers three choices :
Add Modify or Delete.

Add: Fill in the blanks and press the Add To File button to add the displayed record to the file. 
Modify: Change some or all of the entries and press Modify to replace the old entry with the new one.
Delete: Press the delete button to remove the displayed record from the file. Do not edit any of the entries before deletion as the program cannot delete something that isn't there.

When adding/modifying a record with/to a new composer name the user is offered a choice to continue or return to the editor.
Each option from 'File' also offers a quit button this returns to the dummy frame to allow a further selection from the File menu.
Simple.



		

