Why Turbo Pascal programmers
should consider using Turbo Vision

 
Turbo Vision is perfectly suited for office type programs. 
Turbo Vision is much more stable and reliable than Windows programming. 
Turbo Vision can prepare you for Windows / Delphi programming with Objects. 
-
What you get with Turbo Vision Of course, there are some restrictions with the Text-Mode Turbo Vision compared with Windows programming: What you need
A lot of FAQ's become obsolete with Turbo Vision, eg. 
  • how to save a screen area for a window
  • how to implement a mouse and function keys
  • how to make a pulldown menu
  • how to manage (sorted and unsorted) lists of records
  • how to program a comfortable input line dialog
  • how to run a background job (eg. display window)
  • how to program a tiny texteditor for a memo
  • how to make a helpfile for my application program
  • how to save the complete program status on disk
  • etc.
I suggest that you start with Real Mode (not DPMI) for learning.
You shall be aware, that the programming method with Turbo Vision is different from the usual Pascal programming. You do not write procedures and then a menu to make them available to the user. With Turbo Vision you start with the desktop, the menus and windows and fill them with your particular applications. Of course you have to fix where in the window a particular button or listbox shall be placed, and give it a size. But this is never done with the GotoXY(X,Y) procedure, instead you force the button to draw itself on its place on the window. And you do not even force the button, it is automatically performed when you tell the particular window to draw itself, including all other elements in the window.


Do not re-invent the wheel,
use Turbo Vision for your office application programs!

Turbo Vision needs good knowledge and practice with object oriented programming. And it is a very good tutor for object oriented programming too! An old Pascal programmer may think that object oriented programming especially with the virtual methods were opposed to the stringent type checking behaviour of Pascal, yet it is not really, it is an extension and good utilisation of the Pascal strength.

Turbo Vision programs can easily run on Multiuser - Multitasking operating systems, where terminals are connected to the host computer with a serial interface or modems. I am using the REAL/32 Multiuser OS successfully. And I implemented reliable inter-process communication mechanisms with the built in queues and flags and with common files.

Turbo Vision does not waste so much computer power and resources as Windows does for pretty paintings. Turbo Vision is running even on monochrome (Hercules type) monitors / terminals.

Just an example - here you see, what you get (the window frames and some colours are non-standard):
..
Note that the Turbo Pascal IDE is written with Turbo Vision.
The AAG program is a tiny office type program for the boss. It is in German. You can download
a demo version (no source) from my professional homepage.

In most cases the end user prefers stable operation, even if he/she wanted a Windows approach in the first discussions, because somebody told him/her, that this was "modern" and "state of the art". Is the Damokles sword of crashing the system "state of the art", even if another program did it?

Franz Glaser, Austria  Turbo Pascal linkpage
 
 
Program SortFile;

(* Demonstration program. Sort all records of a file,
   using the TSortedCollection object from Objects.PAS (TP6..)

   This example is made to show how easy the Turbo Vision objects
   can be used in an application. It is not meant as a suggestion
   to sort database files usually (like good old dBase did), instead
   some kind of indexing is the state of the art.

   Franz Glaser  *)

Uses Objects;    {Turbo Vision}

Type Str27  = String[27];
     Str63  = String[63];
     Str79  = String[79]; {in unit DOS: PathStr}
     PKeyRec = ^KeyRec;
     KeyRec = Record  {used for sorting, in RAM}
                Rec : Longint; {an integer would be sufficient here}
                Key : Str27;
              End;  {uses 32 bytes total.
                    It is suggested to use records with multiples of
                    8 bytes on the heap}
     FilRec = Record {very primitive, example file record}
                StatusNum : Longint;
                Name1     : Str63;
                Name2     : Str63;
                Age       : Integer; {a byte would be sufficient...}
                Address   : Str79;
              End;
     SorTyp = (NameSort,AdrSort,AgeSort);

{create a derivative of TSortedCollection, which "knows" how to handle
 the particular job}

     PSortColl = ^TSortColl;  {put your flesh around the bones}
     TSortColl = Object(TSortedCollection)
                   Sor : SorTyp;
                   Constructor Init(ASor : SorTyp; ALimit,ADelta:Integer);
                   function Compare(Key1, Key2: Pointer): Integer; virtual;
                   procedure FreeItem(Item: Pointer); virtual;
                 End;

(* implementation  if a unit  *)

Var  SColl : PSortColl;
     FSiz  : Longint; {max. 16000!}
     F1,F2 : File; {source file, intermediate dest.file}

(* three virtual procedures declare the special behaviour of
   our sorted collection. *)
Constructor TSortColl.Init(ASor : SorTyp; ALimit,ADelta:Integer);
Begin
  TSortedCollection.Init(ALimit,ADelta);
  Sor := ASor;
  Duplicates := true; {allow more records with same Age etc.}
End;
{note that it is not really necessary to redefine the Init constructor
 in this example. Setting Sor and Duplicates could have been done
 simply in the main program after the New ... ,Init....
 But it is more elegant to put this setup into the Init constructor,
 so the programmer cannot forget it accidentally...}

(* Compare and FreeItem MUST be specified (redefined) in any
   derivative of TSortedCollection! *)
Function TSortColl.Compare(Key1, Key2: Pointer): Integer;
Var KKey1 : PKeyRec absolute Key1;
    KKey2 : PKeyRec absolute Key2; {one of the possible ways to
                                    typecast an untyped "Pointer"}
Begin
  {case Sor of   not used here, for ref only.
                 Useful if more than 1 key in the keyrec}
  if KKey1^.Key = KKey2^.Key then
    Compare := 0
  else
    if KKey1^.Key > KKey2^.Key then
      Compare := 1
    else
      Compare := -1;
{possible improvement: case Sor of AgeSort: if ages are equal, then
 sort by names. Would need another KeyRec: both age and key-string}
End;
Procedure TSortColl.FreeItem(Item: Pointer);
Begin
  FreeMem(Item,sizeof(KeyRec)); {tell TSortColl.Done how big
                                 the records are}
(*Dispose(PKeyRec(Item)); {another possible solution} *)
End;
{---- TSortColl virtual procedures finished -----------------------}

Function UpcaseStr(S:String):String; {aux function for easy sorting}
Var I : Integer;
Begin
  if S > '' then
    For I := 1 to length(S) do
      S[I] := Upcase(S[I]);
  UpcaseStr := S;
End;

Procedure ReadOriFile; {read unsorted records, build sorted list}
Var R : Longint;
    HaveRead : Integer;
    Cus  : FilRec;
    P : PKeyRec;
Begin
  {assumed: Record Nr. 0 is a special record for database management,
            eg. linked list of erased records and other stuff.
            All valid records have StatusNum=0 else StatusNum=link
            to the next erased record }
  Seek(F1,1); {skip rec. 0}
  for R := 1 to FSiz-1 do
    Begin
      BlockRead(F1,Cus,1,HaveRead); {read from unsorted file}
      if (HaveRead = 1) and (Cus.StatusNum=0) then {valid record read}
        Begin
          New(P); {key - record}
               {here some test could be useful: if P <> Nil then...}
          with Cus, P^ do
            Begin
              Rec := R;
              Case SColl^.Sor of
    NameSort : Key := UpcaseStr(Name1+' '+Name2); {Lastname+' '+Firstname}
     AdrSort : Key := UpcaseStr(Address);
     AgeSort : Str(Age:3,Key); {for alpha sorting}
              End; {case}
            End;
          SColl^.Insert(P);  {sort the key into the collection}
        End;
    End;
End; {readorifile}

Procedure CopyToDestFile; {copy the valid records to the intermediate file}
Var Cus : FilRec;
    HaveRead,HaveWritten : Integer;
  Procedure CopyRecord(P : PKeyRec); Far;  (* !!! Far !!! *)
  Begin
    Seek(F1,P^.Rec);
    BlockRead(F1,Cus,1,HaveRead);
    if HaveRead=1 then  {what else?}
      BlockWrite(F2,Cus,1,HaveWritten);
  End;
Begin
  fillchar(Cus,sizeof(Cus),#0); {management record}
  Cus.StatusNum := -1; {see below}
     {any special data for record #0 here  -> Cus ...}
  BlockWrite(F2,Cus,1,HaveWritten);
  SColl^.ForEach(@CopyRecord);
       {== for I:=0 to SColl^.Count-1 do
           CopyRecord(PKeyRec(SColl^.At(I))) }
End; {copy2destfile}

{main SortFile}
Begin
  Assign(F1,'CUSTOM.DAT');
{$I-}  Reset(F1,sizeof(FilRec)); {$I+}
  if IOresult <> 0 then Halt(55);
  FSiz := FileSize(F1);
  if (FSiz < 2) or (FSiz > 16000) then {16383 limitation with TCollection}
    Begin
      Close(F1);
      Halt(55);
    End;
  SColl := New(PSortColl,Init(NameSort,FSiz,4)); {or another sort-typ}
  ReadOriFile; {read all records, create key, sort key into collection}
  if SColl^.Count = 0 then
    Begin
      Close(F1);
      Dispose(SColl,Done);
      Halt(42); {there was no valid record}
    End;
  Assign(F2,'CUSTOM.$$$');
  Rewrite(F2,sizeof(FilRec));
  CopyToDestFile;
  Dispose(SColl,Done); {will dispose all key-recs automatically}
  Close(F1);
  Close(F2);
  Erase(F1);
  Rename(F2,'CUSTOM.DAT');
End.
=======================================================================
Additional explanation to the 0 - record:

With TAccess.PAS, the Borland database - toolbox unit, the data file
has a record #0 with special data. The most important contents is the
chained list of erased records, where each erased record has a longint
in the first 4 bytes as a link to the next erased record. In record
#0 this longint points to the first erased record or -1 if no erased
record is stored. The last record has -1.
Valid records can use the first 4 bytes for anything else, but I
found it very useful to dedicate it for this purpose, filling the
link (named StatusNum) with 0 whenever a valid record is written to
the database file. This is evident in case of disturbed index files.

This are rather low level considerations, compared with the stuff,
which Delphi programmers are dealing with.

 

LOOK ALSO FOR THE DATAFILE UNIT
Franz Glaser, Austria  Turbo Pascal linkpage



c

Get your own FREE HOMEPAGE