TURBO VISION. ------------- Introduction. ------------- Since Turbo Vision makes use of object-oriented techniques, including inheritance and polymorphism, it is necessary to be completely familiar with object-oriented programming. Furthermore the object instances are dynamically allocated on the heap, so that familiarity with pointers and dynamic variables is also essential. The extended syntax of the 'New' procedure, which allows the allocation of space on the heap for an object and its initialization within the one procedure must also be appreciated. This new procedure is now invoked with two parameters, the pointer name as the first parameter and the constructor invocation as the second parameter. All these topics are covered in the notes on Advanced Data Structures and Object-Oriented Programming. In OOP parlance, an 'object type' is an abstraction that provides a template for specific objects, which are themselves called 'instances' of that object type. In Turbo Vision, the templates or object types all start with the letter T, whilst all pointer types start with the letter P. Both the object instances and the actual pointer variables have names appropriate to the circumstances of use. Thus typical variable declarations are: VAR Window: PDemoWindow; {see page 32 of the Turbo Vision Guide} R: TRect; The extended syntax of 'New' also allows it to be used as a function, returning a pointer value. An example taken from page 32 of the Guide is: Window := New(PDemoWindow, Init( R, 'Demo Window',WinCount)); | | / \ | | Pointer Pointer Constructor Object Title Number variable type invocation of type TRect Thus Window is a pointer of type PDemoWindow, which itself has been previously declared as: TYPE PDemoWindow = ^TDemoWindow; { a pointer to type TDemoWindow } TDemoWindow = OBJECT(TWindow) { a simple user-defined descendant } END; { object of TWindow } Hence Window points to an object type TDemoWindow, which inherits the fields and methods from the ancestor object type TWindow (see pages 321-325). The 'Init' method for TWindow is a static method and is defined on page 322. Three parameters are required, namely the bounds, a title and a number. The bounds are given by the parameter R, which is of type TRect (see page 278), whose data fields define the corners at the top left (A) and the bottom right (B) of the rectangle. One of TRect's (static) methods is the Assign procedure, which gives the coordinates to the points A and B as XA, YA, XB, YB (each of type integer). The assignment is made using the conventional dot notation, so as on page 32 the statement is: R.Assign(0, 0, 26, 7); { set initial size and position } The second parameter to a TWindow initialization is a literal title (in single quotes) and this will be displayed at the top of the window, along with the third parameter, the WinCount number. The declarations and assignments shown in the above few paragraphs illustrate the existence in Turbo Vision of predefined objects, each with its appropriate fields and methods, some of which are static and may be used directly, whilst others are virtual and may be overridden. The way in which objects inherit from ancestor types is indicated in the Turbo Vision object hierarchy shown on page 66 of the Turbo Vision Guide. It is seen that the object type TWindow referred to in the preceding paragraphs is part of this hierarchy tree: TObject --- TView ----- | |-- | |-- | |-- | |-- TGroup ----- |-- |-- TWindow ----- |-- Each of these object types is sufficiently defined for any user application in Chapter 13 'Object reference' on pages 205-325. The actual code is only accessible as a compiled unit (.TPU file), the name of which is also shown in the reference. Thus the 'root' ancestor TObject (pages 267-8) is shown to be part of OBJECTS.TPU, to have no fields and just three methods, Init, Free (both static) and Done (virtual). Apart from TPoint and TRect (see below) all Turbo Vision's standard objects are ultimately derived from TObject. Any object that uses Turbo Vision's streams facilities must trace its ancestry back to TObject. TView is found in VIEWS.TPU, it has 11 fields, including size and options, and 64 methods, including Init, Done, Draw, Show and WriteLine (pages 306-321). TGroup is also in VIEWS.TPU, it has 4 fields and 29 methods (pages 235-244). Because units are used, it follows that there must be a USES statement at the start of any application program to ensure that all the required TPUs are called. Chapter 12 'Unit cross reference' (pages 189-204) gives details of all the units used by Turbo Vision. Types, constants, variables, procedures and functions are listed for each unit. Because of inheritance, it is possible that a method used by any object type may not be its own, but that of an ancestor type. It may therefore be necessary to refer back through the hierarchy to find the definition of a particular method. TPoint is a simple object type representing a point on the screen by its only two fields X and Y. It has no methods and is thus only a record and is the ultimate abstraction as far as screen display is concerned. TRect has two fields, A and B, both of type TPoint and 9 methods. Apart from the standard object hierarchy of Turbo Vision, there are a number of other elements (types, constants, variables, procedures and functions) which are defined in the Turbo Vision units. These are listed in Chapter 14, 'Global reference' on pages 327-384 and include for example: TYPE PString defines a pointer to a string OBJECTS.TPU PtrRec record of ofs & seg of a pointer OBJECTS.TPU CONSTANT hcXXXX help context constants VIEWS.TPU ofXXXX options flags VIEWS.TPU VARIABLE ScreenHeight height in lines of current screen DRIVERS.TPU MenuBar stores a pointer to menu bar APP.TPU PROCEDURE DisposeMenu disposes all elements of the menu OBJECTS.TPU ClearScreen clears the screen DRIVERS.TPU FUNCTION NewStatusDef returns a pointer to a new TStatusDef record MENUS.TPU Turbo Vision examples. ---------------------- Now that the structure of Turbo Vision and the notation has been presented, it is possible to proceed with discussion of a specific example, which relates to a screen display like that of the Turbo Pascal Integrated Development Environment. The display has a working area called the Desktop, which occupies most of the screen, a Menu Bar at the top and a Status Line at the bottom, which provides the user with advice on how to proceed, even if it is only - Alt-X Exit. The example is described in detail in Chapter 2 of the Turbo Vision Guide (pages 23- 28). The program is in TVGUID01.PAS which is included with the demo programs on the distribution disks and is listed below: PROGRAM TFirst; USES App; { application objects are in APP.TPU } TYPE TMyApp = OBJECT(TApplication) { define new application type leaving } END; { room for future expansion } VAR MyApp : TMyApp; { create an instance of the new type } BEGIN MyApp.Init { set it up } MyApp.Run { interact with the user } MyApp.Done { clean up afterwards } END. At present the new object type TMyApp is no different to TApplication, but it will be in due course when the methods of TApplication are overridden. The default behaviour of a TApplication produces a simple screen with a blank menu bar, an empty desktop and a status line with only 'Alt-X Exit'. These screen areas have been created by the TApplication (virtual) methods InitDeskTop, InitMenuBar and InitStatusLine, which are inherited from TProgram, the ancestor type to TApplication (see page 272). Whereas the DeskTop initialization is seldom overridden, the other two are always overridden in any meaningful application. The object type TApplication is a simple 'wrapper' around TProgram and only differs from TProgram in its constructor and destructor methods. TApplication.Init first initializes all Turbo Visions subsystems (the memory, video, event, system error and history list managers) and then calls TProgram.Init. Likewise, TApplication.Done first calls TProgram.Done and then shuts down all Turbo Vision subsystems. Thus the new descendant type TMyApp has an Init method, MyApp.Init, which will call the inherited TApplication.Init and then call the initialization code specific to the new application, overriding some of the inherited virtual methods. An example of a new virtual method is illustrated in TVGUID02.PAS, which has a procedure InitStatusLine; virtual; added to the type declaration of TMyApp. This new initialization procedure (shown on the next page) involves a sequence of nested calls to standard Turbo Vision functions NewStatusDef and NewStatusKey. The return values to these functions are PStatusDef, which points to a type TStatusDef, and PStatusItem, which points to a type TStatusItem, respectively. The TStatusDef type represents a status line definition and is a record type declared in MENUS.TPU as follows: TStatusDef = RECORD Next : PStatusDef; {points to next TStatusDef in a list or NIL} Min, Max : Word; {define range of help contexts for status line} Items : PStatusItem; {points to a list of status line items or NIL} END; The TStatusItem type represents a status line item that can be visible or invisible and is a record type declared in MENUS.TPU as follows: TStatusItem = RECORD Next : PStatusItem; {points to next TStatusItem in a list or NIL} Text : PString; {points to string containing status item legend} {such as 'Alt-X Exit' or NIL if item invisible } KeyCode : Word; {contains scan code of hot key for item or zero} Command : Word; {contains command event generated when selected} END; The linked lists involved in the above two record types were described in the notes on Advanced Data Structures, to which reference should be made if necessary (HEAP&PTR.TXT on the Turbo Pascal Tutor diskette). The NewStatusDef function (page 360) allocates and returns a pointer to a new TStatusDef record. The function declaration is: FUNCTION NewStatusDef(AMin, AMax: Word; AItems: PStatusItem; ANext: PStatusDef): PStatusDef; The record is initialized with the given parameter values. Calls to NewStatusDef and NewStatusKey can be nested to create entire status line definitions in one Pascal statement as shown in the example below. The NewStatusKey function (page 360) allocates and returns a pointer to a new TStatusItem record. The function declaration is: FUNCTION NewStatusKey(AText: String; AKeyCode: Word; ACommand: Word; ANext: PStatusItem): PStatusItem; The record is initialized with the given parameter values. If AText is empty, the status item is hidden, but will still provide a mapping from the given KeyCode to the given Command. The important statement in the InitStatusLine procedure involves the StatusLine variable of type PStatusLine (page 372) which stores a pointer to the application's status line. TStatusLine is an object type defined in MENUS.TPU and described on pages 292-294. It is initialized as follows: CONSTRUCTOR Init(var Bounds: TRect; ADefs: PStatusDef); Thus using the extended form of the New function with the constructor invocation as the second parameter, the form of the statement is StatusLine := New(PStatusLine, Init(VAR Bounds: TRect; ADefs: PStatusDef); where the Bounds of type TRect is simply R and ADefs of type PStatusDef is obtained as the value returned by the function NewStatusDef (see above), whilst the third parameter of this function AItems of type PStatusItem is obtained as the value returned by the function NewStatusKey (see above). In this case the fourth parameter of the function NewStatusKey which is ANext of type PStatusItem can itself be obtained as the value returned by another call to the function NewStatusKey and so on if necessary until a pointer value of NIL is encountered. Thus the complete InitStatusLine procedure becomes: PROCEDURE TMyApp.InitStatusLine; VAR R: TRect; {holds the boundaries of status line} BEGIN GetExtent(R); {set R to coordinates of full screen} R.A.Y := R.B.Y - 1; {move top to one line above bottom} StatusLine := New(PStatusLine, Init(R, {create status line} NewStatusDef(0, $FFFF, {set range of help contexts} NewStatusKey('~Alt-X~ Exit', kbAltX, cmQuit, {define item} NewStatusKey('~Alt-F3~ Close', kbAltF3, cmClose, {another} nil)), {no more keys} nil) {no more defs} )); {bracket closures for Init and New} END; It is also necessary to add PROCEDURE InitStatusLine; VIRTUAL; to the declaration of TMyApp. The initialization is a sequence of nested calls to standard Turbo Vision functions NewStatusDef and NewStatusKey. The program defines a status line for a range of help contexts from 0 to $FFFF and binds the standard Turbo Vision command cmQuit to the Alt-X keystroke and the standard command cmClose to the Alt-F3 key. There is no need for the InitStatusLine method to call the method it overrides, TApplication.InitStatusLine, as there is nothing in the overridden method that would help. The part of the string 'Alt-F3 Close' enclosed within the tildes(~) will be highlighted on the screen, once a window has been opened and will allow a mouse-click activation. If no window is open then it is not highlighted and the cmClose command is disabled by default. The commands cmQuit and cmClose are standard Turbo Vision commands and do not require the user to define them. Customized commands are declared as constant values, in the ranges 100-255 (disable) and 1000-65535 (non-disable). Thus the following lines can be added to the above program: CONST cmNewWin = 199; ..... ..... NewStatusKey('~F4~ New', kbF4, cmNewWin, { bind new command} ..... ) { close bracket after first NIL} The Menu Bar is slightly more complicated than the Status Line, because of the pull-down sub-menus, but is similarly initialized with nested calls to the standard Turbo Vision functions NewMenu, NewSubMenu, NewItem and NewLine. The MenuBar variable (page 355) stores a pointer (of type PMenuView) to the application's menu bar (of type TMenuBar), a descendant of TMenuView. It is defined in MENUS.TPU and is initialized as follows: CONSTRUCTOR Init(VAR Bounds: TRect; AMenu: PMenu); Thus using the extended form of the New function with the constructor invocation as the second parameter, the form of the statement is: MenuBar := New(PMenuBar, Init(VAR Bounds: TRect; AMenu: PMenu); where the bounds of type TRect is again R and in this case AMenu, of type PMenu, is obtained as the value returned by the function NewMenu (page 359), which requires a parameter 'Items' of type PMenuItem. This parameter of type PMenuItem is itself returned by the function 'NewSubMenu' (page 361), which requires as its parameters: Name: TMenuStr; { a defined string type of 31 characters - p 378} AHelpCtx: Word; { a help context constant } SubMenu: PMenu; { value returned by function NewMenu - p 359 } Next; PMenuItem; { value returned by function NewItem - p 359 } The function NewItem is declared as follows: FUNCTION NewItem(Name, Param: TMenuStr; KeyCode: Word: Command: Word; AHelpCtx: Word; Next: PMenuItem): PMenuItem; from which it is clear that NewItem may call itself for the last parameter. The nested structure of these various function calls can be illustrated as follows: MenuBar allocation and initialization requires the parameter PMenu from the function NewMenu and Function Parameter obtained from Return value ------------------------------------------------------------------ NewMenu 1 - PMenuItem NewSubMenu PMenu NewSubMenu 3 - PMenu NewMenu PMenuItem 4 - PMenuItem NewSubMenu (another) NewItem 5 - PMenuItem NewItem (another) PMenuItem Applications to show windows in the desktop area of the screen are illustrated in the demonstration programs TVGUID04.PAS to TVGUID10.PAS, but contain only a few new concepts. Apart from Event Handling to be discussed later, the following new objects and statements are introduced: A TWindow object which is a specialized group that typically owns a TFrame object, an interior TScroller object and one or two TScrollBar objects. These attached subviews provide the "visibility" to the TWindow object. The TFrame object provides the border, a place for an optional title and number and functional icons (close, zoom, drag). TWindow objects have the "built-in" capability of moving and growing via mouse drag or cursor keystrokes. They can be zoomed and closed via mouse clicks in the appropriate icon regions. They work with scroll bars and scrollers. Numbered windows can be selected with ALT-n keys (n = 1 to 9). TWindow initialization requires three parameters: Bounds of type TRect, a title of type TTitleStr and a window number. TDeskTop is a descendant of TGroup and inherits the procedure Insert, which requires a parameter of type PView. Thus the statement to place a window in the desktop is: DeskTop^.Insert(Window) where Window is a pointer Any number of views, called subviews, can be inserted into a group object like desktop, which is called the owner view. To place something into a window, a view called an interior is created and then inserted. An object TInterior, a descendant of TView, with its own initialization constructor and own draw procedure is introduced. The WriteStr procedure, inherited from TView, is added to the TInterior.Draw procedure. The TInterior initialization includes the GrowMode variable, which ensures that the subview will grow as the owner view is resized. TVGUID05.PAS displays a Demo Window, with "Hello World" inserted. Frequently it will be necessary to create a procedure 'ReadFile' to read a file for insertion in a window. The normal assign and reset statements are followed by a while loop to read each line and dynamically allocate heap storage, returning a pointer to the first byte of each string, using the NewStr function. This is illustrated in the program TVGUID06.PAS and shown on page 39 of the Turbo Vision Guide. Another procedure 'TInterior.Draw' then uses the standard procedure MoveChar (page 358) to move spaces into a buffer, prior to using another standard procedure MoveStr (page 358) to move a string into the buffer and then uses the WriteLine procedure, inherited from TView, to write the line contained in the buffer to the screen (page 321). This procedure is used in TVGUID07.PAS and is shown on page 40 of the Guide. In order to see the whole file on the screen is necessary to be able to scroll up and down, with the mouse reacting with scroll bars at the right and bottom of the view. In this case the object type TInterior is a descendant of TScroller, instead of TView, as above. The new constructor invocation has a bounds parameter and also two pointers of type PScrollBar, namely HScrollBar and VScrollBar, which point to the horizontal and vertical scroll bars respectively. A procedure TDemoWindow.MakeInterior uses the function 'StandardScrollBar', inherited from type TWindow, to create, inset and return a pointer to a standard scroll bar for the window. The constructor invocation for TScroller uses the parameters Bounds, HScrollBar and VScrollBar and is called within the New function in order to obtain the pointer to the 'Interior'. Finally the insert procedure, inherited from TGroup, is called to insert the interior. TVGUID08.PAS provides an illustration of a scrolling interior and is shown in part on pages 42-44 of the Guide. Finally, a program provided on Borland's Install diskette, TVDEMO.PAS, can be modified by the user for his own purposes, using the above information to create a menu-bar, desk-top and status-line display for any number of text files, including source code, etc. The writer of these notes has made use of this program for this tutorial display. TVISION.TXT 20.3.91 Revised 12.6.93