home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_GEN / TVGRAPH.ZIP / BEGINNER.DOC (.txt) < prev    next >
WordStar Document  |  1993-11-12  |  29KB  |  453 lines

  1.  
  2. Beginner's Turbo Vision/TVGraphic Tutorial
  3.  
  4. This file attempts to give a brief once over of areas that trouble Pascal programmers when working with Turbo Vision and TVGraphic. (TV is used when referring to both.)
  5.  
  6.  
  7. Contents
  8.   Basics on Pointers in TV
  9.   PStrings and Memory Overwrites
  10.   TV/TVGraphic - Order of Views 
  11.   Inserting Views - Assumptions, Where, How
  12.   Color Palettes
  13.   Communication Among Views in a Dialog Box (TDialog)
  14.  
  15. Three things combine to make the transition to Turbo Vision and similar environments difficult - Object orientation, event driven behavior and pointers. Tutorials on objects, event driven environments and pointers are in Borland's doc but thinking Turbo Vision "pointer" isn't. That's here.
  16.  
  17. The reward for learning Turbo Vision and TVGraphic is the high quality of programs you can turn out. There is far more to them than just a pretty face. Menus, windows and screen redraws are handled fully automatically. The event driven framework is sophisticated and flexible. Skim the Borland manuals on Collections, Streams and Resources. These are very powerful data handling tools and they are integrated in. No longer must files contain records of the same length unless working at the binary level. Collections provide this benefit and more for items in memory. Much of this new Pascal power comes from pointers. 
  18.  
  19.  
  20. BASICS ON POINTERS IN TV
  21.   In much traditional Pascal programming, the idea was to avoid using pointers unless absolutely necessary. This was a virtue to the extent that it avoided the many bugs and system crashes which pointers lend themselves to. However pointers are the only way to access the true power of object orientation in Pascal and C++. Only a pointer to an object can allow you to write code that works with any object type a user can select at run time. Yes, Pascal object pointers are typed, but an object pointer can point to an object of its own type or to an object of a descendant type. Your code will work with either at run time. See the section of the Pascal manuals that has a general tutorial on pointers.
  22.  
  23.   If you haven't worked with pointers and look at the source code for Turbo Vision, parts of it will be almost unrecognizable. In fact these parts of Turbo Vision don't feel as if they were written by a Pascal programmer - more like a pointer maniac. 
  24.  
  25.   Let's look at ways of thinking that may help.
  26.   A pointer is just the numerical address (location) of a data structure (char,integer,record,object instance) in memory. It is the way programs, at the internal level, actually know where they put their variables in memory.
  27.  
  28. Object Types (definitions) are usually called "objects" in Pascal (they are called classes in other object oriented languages). An object type is similar to a Record in that it contains data fields but it also defines Functions and Procedures which are contained in itself. Then there are actual individual object variables, each created in its own space in memory. These are also called objects. Confusing. Finally there can be a pointer type defined for any particular object type. Pascal will enforce type checking to be sure all these things match.
  29.  
  30.   Turbo Vision speak:
  31.  
  32. 1. Assume in TV that there is a pointer type defined for every object type. (Follow this practice yourself.)
  33.  
  34.     TView - T indicates an object type  (a class)
  35.     PView - P indicates a pointer type. PView type pointers point to TView type objects (and their descendants).
  36.  
  37.   example    
  38.      type           {Pascal allows this out of order declaration 
  39.                        within a single Type statement}
  40.        PView = ^TView  
  41.        TView = Object 
  42.                  Owner : PGroup;
  43.                  Size : TPoint; {TPoint is a record with fields
  44.                                        x,y}
  45.                   ...
  46.                   ...
  47.                  end;
  48.  
  49.  
  50. !  NOTE THE ^ SYMBOL. It is Critical to Understanding !
  51.  
  52.      With pointers and variables (and their types), the ^ lets 
  53.      you grab one if you know the name of the other.
  54.     
  55.            ^Variable   is a pointer to Variable
  56.             Pointer^   is the variable that Pointer points to.
  57.     
  58.          Think about this for a minute.
  59.  
  60. 2.  Whenever you see a variable representing an object in Turbo Vision, EXPECT it is a pointer to the object (rather than the object itself). This is correct 90-99% of the time.
  61.  
  62.        example
  63.          var
  64.            MyView : PView;
  65.            x      : integer;         begin
  66.            MyView := New(PView, Init(.......));
  67.            x := MyView^.Size.x;
  68.  
  69. MyView is a pointer variable - commonly called a pointer. The first statement after Begin creates an object of type TView in memory and then assigns the address of this memory to pointer  MyView.
  70.  
  71. Turbo Vision's TView has a field Size which contains a record with x and y fields. 
  72.  
  73.   So     "Size.x"      by itself should look familiar. 
  74.  
  75.   What may not look familiar is   MyView^  . 
  76.  
  77.   And how about MyView^.Owner^.Size.x  ?
  78.  
  79. Welcome to Turbo Vision and the land of pointer.
  80.  
  81.   Take a deep breath. Exhale. 
  82.   Ingest your favorite mood altering substances. 
  83.   Say: It will all make sense.
  84.  
  85. We said MyView^ meant the variable (object) pointed at by MyView
  86.     
  87.   so  MyView^.Size  
  88.        says that Size is a field in variable (object) MyView^.
  89.        Remember MyView^ is an object of type TView.
  90.        (Think of the object as a Record with various fields)
  91.   
  92.   In TV, each TView has a field Owner which is of type PGroup.
  93.   TGroups are descended from TViews.
  94.  
  95.   then  MyView^.Owner^.Size
  96.  
  97.      will be the Size field of the Owner^ (TV jargon) of MyView^.
  98.        That is, Owner is a PGroup pointer. So Owner^ is another 
  99.        object of a type descended from TView. 
  100.        It therefore has ITS OWN Size field!
  101.        THAT Size field is:    MyView^.Owner^.Size !
  102.  
  103. 3.        See >    MyView^.Owner^.Size   
  104.           
  105.             Think >    MyView's Owner's Size
  106.  
  107.   Since Size is just a record type variable, the  x  field of Size is as usual -  Size.x
  108.  
  109.   Again, MyView and Owner are the pointers, MyView^ and Owner^ are the actual objects.
  110.  
  111. If you can start to think as in 3 above, you are over the hardest part.
  112. Take a Break
  113.  
  114. Continuing
  115.  
  116.  
  117. HOW TO CRASH YOUR COMPUTER, two ways, no sweat
  118.  
  119.     MyView^.Owner^.Size
  120.  
  121.   When you initialize an object in TV, all the fields are set to 0 (Magic done in TObject.Init). This includes pointer variables like Owner in MyView/TView. This is referred to as a nil pointer - it doesn't point at anything real. 
  122.  
  123. BUT it still acts as a pointer. If Owner is nil and your code attempts to read Owner^.Size.x, it will find a value. Not one you can predict but not usually dangerous. 
  124.  
  125. However there are functions and procedures attached to a TView object and it is often necessary to call them through an object's pointer. It is very common for TV code to call say
  126.  
  127.                     Owner^.MoveTo 
  128.  
  129. Which tells object Owner^ to execute its MoveTo procedure (where MoveTo is a procedure defined in object type TView). If pointer Owner is nil, the call proceeds anyway (the compiler knows the type of Owner and so can find most of its methods). But if MoveTo is a "virtual" method (see Borland Doc) or if MoveTo in turn calls any virtual methods, then Pascal will jump to the method based on a table of procedure starting addresses near address nil=0 of memory. Wherever an object's virtual method table is, it is guaranteed not to be there. Boom.
  130.  
  131. >>> Calls to virtual methods of uninitialized objects can be trapped as a runtime error if the compiler's Range checking is turned on.
  132.  
  133. The other common way to crash also comes from pointer fields in objects. Say you sometimes create a TCollection and assign it to the pointer MyView.CollectionPtr and sometimes not. When you call MyView.Done, you need to Dispose of the TCollection. If you don't check whether the pointer to TCollection is nil, sometimes you will try to dispose a nil pointer. Boom.
  134.  
  135. The only real protection is to be methodical about testing whether pointers are set to nil before calling "Pointer^.procedure" or disposing pointers.
  136.  
  137. 4.A. If you have a pointer in one object to another object, assign a valid value to this pointer in the Init constructor if possible. (It isn't always possible/desirable.) If you leave the pointer = nil in MyView.Init, you must later be perfect in testing whether the pointer is still nil. Turn RangeChecking on for the first month or two.
  138. 4.B.  In your Done methods, ALWAYS check for a nil pointer before Disposing objects you created but didn't insert into the view. Otherwise you will find that a front panel Reset switch on your computer is very important in the learning phase of Turbo Vision. 
  139.  
  140. As a Pascal programmer you are just not used to thinking like this. So save your source code to file BEFORE you run. After a while this diligence in assigning values to pointers and testing for nil becomes automatic. Sounds like C, no?
  141.  
  142. 5. Pointer Safety in Construction 
  143.        {note: a TGroup is a descendent of TView}
  144.         var
  145.           MyGroup : PView; {can point to any descendent of TView}
  146.         begin
  147.             .....
  148.             .....
  149.        >    MyGroup := New(PGroup, Init(.......));  <
  150.  
  151.          !!! ALWAYS do Construction as above !!!
  152.  
  153. Using the NEW function in the special way above combines allocating the memory for a new object with calling its constructor Init to initialize the object and build its virtual method table.
  154.  
  155.   Yes there are other ways.
  156.               New(MyGroup, Init(.........));
  157.   Problem : Assuming you wanted a TGroup, you just constructed
  158.   and initialized a TView since MyGroup is of type TView. What-  
  159.   ever type pointer MyGroup is will determine what type of object 
  160.   you will construct. If the variable declarations are not on 
  161.   screen, it is easy to forget what type MyGroup really is.
  162.  
  163.                MyGroup := New(PGroup);
  164.                MyGroup^.Init(........);
  165.   NEVER - if you forget to call .Init before you call a method of 
  166.          MyGroup, could bomb - Reset switch time.
  167.  
  168.  
  169. Aside - There must be a way around all these pointers.
  170.   Now it has occurred to some of you (it did to me) that one can avoid some of the pointer experience when using standalone objects by doing
  171.      var
  172.        MyObj : TView;  {TView is object, NOT a pointer type}
  173.  
  174. at the beginning of a subroutine. The subroutine creates an object of type TView called MyObj and will dispose of it as the subroutine ends. 
  175.   This works fine but you can still get caught if you forget to call MyObj.Init BEFORE you call ANY of the object's methods. If Range checking is off and you call a virtual method, directly or indirectly, then Boom. If Range checking is on, then runtime error if virtual method but no error if ordinary method.   Still your data fields have not been initialized (happens in Init) and if they contain an object pointer, it won't be set to nil. Your safety tests for nil pointers will fail - you could end up disposing a pointer in your Done method that had never been assigned. Boom.
  176.  
  177.  
  178. Break
  179.  
  180. Easy stuff
  181.  
  182. How about the ^Variable usage?   
  183.  
  184. In Turbo Vision it is common to organize your variables (objects) by creating either a linked list of pointers to the variables or
  185. an array of pointers to the variables. This is convenient because it is also usual to create objects as the user needs them (like a File Open dialog box) and then destroy them to regain their memory. If all possible dialogs and other objects had memory allocated at program startup, it would be restrictive given the large size of many of today's DOS programs.
  186.  
  187. So the objects are scattered through memory (unlike an array of objects which would be ordered) but their sizes don't all have to be the same (as for an array of a datatype). Same on disk files. So back to pointers.
  188.  
  189. The ^Variable usage is much less common than the Pointer^ usage. This is simply because most object variables are already pointers in TV. 
  190.      
  191.         APointer := ^AnyVariable;
  192.  
  193.   assigns the address of AnyVariable to APointer.
  194.         
  195.  
  196.   Also you see this all the time in type declarations:
  197.         type
  198.           PMyView = ^TMyView; 
  199.  
  200.   Which declares PMyView to be a pointer to type TMyView.
  201.  
  202. Turbo Vision has alot of procedures that take pointers to strings as a parameter rather than the string itself. It goes against the Pascal grain but that's the way it is. So you will probably use a string and assign it a value in the usual way. Then pass the string as a pointer to the procedure:
  203.          type
  204.            Str20 = string[20];                definition
  205.            PString = ^String;                 definition
  206.  
  207.            procedure DoIt(StrPtr : PString);  definition
  208.            begin
  209.            end;
  210.            ....             
  211.                 Str20  := 'AARDVARK';
  212.                 DoIt(Str20);    {type mismatch error}
  213.                 DoIt(^Str20);   {simple as that}
  214.  
  215.  
  216.  
  217. If you have stuck it out this far, congratulations. Class dismissed.
  218.  
  219. {-------------------------------}
  220.  
  221. PSTRINGS and MEMORY OVERWRITES
  222.  
  223. TV often handles strings by using a pointer to them. It often organizes groups of strings as Collections of PStrings. For example it uses the following function to create PStrings:
  224.         function NewStr(S : string) : PString;
  225.  
  226. So      AStrPointer := NewStr('a string')    
  227.   allocates memory for the string "a string" and returns a pointer to this memory. The amount of memory allocated is just enough to contain the exact string you pass to the function plus one byte for the string's length. Very efficient memory usage.
  228.  
  229. This is fine until you want to change the contents of the string.
  230.  
  231. If you create a regular Pascal string 
  232.  
  233.         MyString : string = 'Longer than a string';
  234.  
  235. and then assign MyString to the string pointed to by AStrPointer
  236.  
  237.        AStrPointer^ := MyString;
  238.  
  239. you Will OverWrite Memory not belonging to AStrPointer! MyString is too long to fit into the memory allocated for AStrPointer but it will be put there anyway. Notice that you have no foolproof way to know at run time how big the memory for AStrPointer is even though Pascal's memory manager knows. Hopefully Borland will come up with something better.
  240.  
  241. There are no error messages or compiler checks.
  242.  
  243. Note: if you want to assign AStrPointer^ a shorter string or one of the same length, you should check first to see if AStrPointer is nil. ( AStrPointer is set to nil if NewStr is passed an empty string.)
  244.  
  245. >> Think twice before changing the contents/length of a string pointer.
  246.  
  247. {-------------------------------}
  248.  
  249.  
  250. TV/TVGraphic ORDER OF VIEWS
  251. The Borland doc never really gets around to nailing down view order. We'll try it here. TV refers to both Turbo Vision and TVGraphic.
  252.  
  253. TGroup's contain TViews. For the purposes of discussion here, a TView is anything visible on the screen. TGroups are not visible except for the Views they contain. The Application (TGroup) contains the Menu, StatusLine and DeskTop. The DeskTop (TGroup) contains a visible Background and the windows (TWindow - descends from TGroup) on screen. These windows contain TViews although in TVGraphic they may also contain TSubWindows, a descendant of TGroup. When thinking of view order, remember that every visible part of TV is a TView.
  254.  
  255. There are two areas that depend on the order of views. One is the handling of events and the other is the drawing of views on the screen. 
  256.  
  257. Terminology
  258.   Z- order : partially defined concept in Borland doc to explain 
  259.              view order
  260.  
  261.   function TGroup.First
  262. Borland = "returns a pointer to the TGroup's first subview, the one closest to the top in Z-order."
  263.      The FIRST subview gets passed keyboard and command events before any other subview. Visually it is the top view on the screen - all other views appear behind it. In Turbo Vision, the First subview draws first (counter-intuitive as that may seem). In TVGraphic, the First subview draws last. A subview newly inserted using TGroup.Insert becomes the First subview.
  264.  
  265.   function TView.NextView 
  266. Returns a pointer to the "Next" subview in the view's Owner. So if you start with the First subview. You can chain through all the subview NextView pointers to reach the Last subview.
  267.   function TView.PrevView
  268. Returns a pointer to the "Previous" view. So you could start at the Last view and chain through all the PrevView subview pointers to the First view. Opposite direction from NextView.
  269.  
  270.   TGroup.Last   pointer field
  271. Points to the Last subview in order. This view receives events last. Draws first in TVGraphic. Draws last in Turbo Vision.
  272.  
  273.   Top
  274. When discussing Z order, the First subview is at the top.
  275.  
  276.   Bottom
  277. When discussing Z order, the Last subview is at the bottom.
  278.  
  279.  
  280.  
  281. Note that the subview list is circular. The TView.Next field of the Last subview points at the First subview. The TView.Prev function of the First subview returns a pointer to the Last subview.
  282.  
  283. {----------------------------}
  284.  
  285. INSERTING VIEWS - Assumptions, Where, How
  286.  
  287. TV is based around certain assumptions about how you will organize your program. You don't have to follow these assumptions but it sure makes life easier.
  288.  
  289. The first assumption is that you will insert all non-window views into a window or a descendant type of a window (dialog). Not into the DeskTop or Application.
  290.  
  291. The second assumption is that you will insert all dialogs into the DeskTop. (ExecuteDialog does this automatically for example).
  292.  
  293. The third assumption is that you will only insert buttons, inputlines, listboxes, labels (objects defined in unit Dialogs that are selectable or cause other objects to be selected) in a dialog.
  294.  
  295. Well what if you don't want to? 
  296.  
  297. First Assumption
  298. In general, mixing selectable views with more than one window in the desktop will interfere with moving between windows using F6, Shift F6. See further discussion in section on TVGraphic's TPanWindow in main doc file.
  299.  
  300. Also inserting views outside the above assumptions will usually cause them to draw in different colors since they map into a different part of the color palette. For example. a dialog's palette has many more entries than a TWindow, including entries to TButtons. If you insert a button into your descendant of TWindow, there are no TWindow palette entries defined for it. You will have to fix that.
  301.  
  302. Second Assumption
  303.   Note : Don't try to insert a dialog into a dialog.
  304. Modal Dialogs: the only two sane choices are inserting it into the DeskTop (DeskTop.ExecView or TProgram.ExecuteDialog) or into the Application. Use the DeskTop. 
  305.  
  306. Non-modal dialogs (persistent dialogs}
  307. Insert into the DeskTop (DeskTop^.Insert). TVGraphic also allows insertion of non-modal dialogs into a TPanWindow.
  308.  
  309. Third Assumption
  310. If you want a button in your DeskTop, make it non-selectable by clearing its sfSelectable bit in its State field. If you want a bank of selectable buttons, think about placing them in a special window. Then insert that window in the DeskTop. For other dialog controls you are on your own. See discussion under First Assumption. 
  311.  
  312. {----------------------------}
  313.  
  314. COLOR PALETTES
  315. TV's color palette system is the part of TV that this programmer finds the most cryptic. There is a discussion on changing palette colors in the main doc file. Here we look at concepts. 
  316.  
  317. Note: It is often easier to avoid using the palette when writing Draw methods for new views that don't resemble standard TV views. Work back toward the palette method of obtaining color as your application firms up.
  318.  
  319. Each type of view has what is called a color palette. Although palettes are  Pascal String types, it is best to think of them as arrays of bytes. Only the TApplication has actual colors in its palette. All the other palettes contain numbers that represent offsets into the palette (array) of the view's Owner. The application palette packs two four bit colors into each byte. Foreground color is the lower 4 bits, background is the upper four bits.
  320.  
  321. In TV, the color a view will use to draw itself depends on what type of view it is inserted into. Example: the background color of StaticText differs depending on whether it is inserted into a window or a dialog. To make this happen, each view's Draw method calls the view's GetColor function to find out what colors to use. GetColor calls its Owner's GetColor recursively  until it reaches the Application. 
  322.  
  323. As GetColor works it way up the view chain, it calls GetPalette at each level and sums the offset value from each palette to get the total offset into the Application palette. (The Desktop GetPalette returns nil - it doesn't have a palette so the offset values GetColor has calculated are not changed by the DeskTop.)
  324.  
  325. Pragmatically:
  326.  
  327. TV2.0 "GrayDialog" and TV1.0 Dialogs:
  328.   The offset in the first palette entry is 32 (valid entries range from 1 up). So anything inserted into a TDialog will have the offsets in its own palette increased by 31. 
  329.   (A subview palette offset of 1 will map to the first dialog palette entry of 32. So 32-1 = 31.)
  330.  
  331. For a "BlueWindow"  
  332.   The offset in the first palette entry is 8. So anything inserted into a "BlueWindow" (actually gray in TVGraphic) will have the offsets in its own palette increased by 7.
  333.  
  334. If you insert a window in a window for example, then entries will be increased by 7 by each window's GetPalette.  This is a source of much confusion since you no longer map to the expected colors. Note: TVGraphic TSubwindows and TVGraphic non-modal TDialog avoid this problem by using modified GetPalette functions.
  335.  
  336.  
  337. Example:
  338.   TInputLine inserted into a Dialog    The second entry in the TInputLine palette is for "Active".
  339.       Its offset is 19.  (Active means the window  TInputLine is 
  340.          inserted into is in the Active state.)
  341.     Draw obtains the active colors by calling GetColor(2).
  342.         (2 for second entry in TIputLine's palette)
  343.     To calculate the offset into the application's Palette, 
  344.       add 31 (from Dialog) to 19 = 50; The colors are the 50th 
  345.       entry in TApplication's palette.
  346.  
  347.       
  348. {----------------------------}
  349.  
  350. COMMUNICATION AMONG VIEWS IN A DIALOG BOX (TDIALOG)
  351.  
  352.   Many View types can be included in a Dialog box and it is helpful to consider the interview communication that takes place. The summary below is true for TVGraphic and is generally true for TV.
  353.  
  354.   Remember - all predefined Dialog boxes in TV are ExecView'd by the Desktop rather than Inserted - this makes them "modal". This means that the Dialog box fetches all Events from the Event queue rather than the Application fetching them. So Event handling begins in TDialog.HandleEvent.
  355.  
  356.   Note that when a Dialog box is modal, there is no real difference in a subView using the Message function to call its Owner and sending a PutEvent - both events end up in TDialog.HandleEvent. (Of course a Message is processed immediately, a PutEvent goes to the rear of the Event queue.) Often the event is of type evBroadcast so it will reach all the Dialog's views in case  more than one needs to react.
  357.   BUT if a Dialog box is Not Modal, an event sent with PutEvent would first show up at TProgram.HandleEvent. If it was an evBroadcast, it would be received by EVERY view in the application!
  358.  
  359.   Note that EndModal returns the terminating command through ExecView or ExecuteDialog.
  360.  
  361.   Summary of commands and messages:
  362.  
  363.   TDialog.HandleEvent(Event)
  364.     if AllowBothMButtons is false (TVGraphic default) then
  365.       right mouse click generates a PutEvent with
  366.             Event.What := evCommand
  367.             Event.Command := cmCancel
  368.             Event.InfoPtr := nil
  369.     call TWindow.HandleEvent, return
  370.     keystrokes:
  371.       kbEsc:   PutEvent   evCommand,cmCancel,InfoPtr := nil;
  372.       kbEnter: PutEvent   evBroadcast,cmDefault,InfoPtr := nil;
  373.     evCommand:
  374.       case Event.Command of
  375.         cmOk, cmCancel, cmYes, cmNo:
  376.           if State and sfModal <> 0        {if TDialog is modal}              EndModal(Event.Command);
  377.              {next part is TVGraphic only}
  378.         else if (VOptions and ReturnOnAnyButton <> 0)
  379.           and (State and SfModal <> 0)
  380.               EndModal(Event.Command);
  381.       end;
  382.  
  383.  
  384.   TButton
  385.    Note: TVGraphic handles the Default button differently from TV
  386.           TV buttons send messages using commands
  387.               cmGrabDefault, cmReleaseDefault.
  388.           TVGraphic does not use these messages or commands.
  389.     .HandleEvent
  390.           various situations call Press
  391.     .Press
  392.       Message      Owner,evBroadcast,cmRecordHistory,InfoPtr=nil
  393.       if bfBroadcast set
  394.         Message    Owner,evBroadcast,buttoncommand,InfoPtr= @Self
  395.       bfBroadcast not set
  396.         PutEvent   evCommand,buttoncommand,InfoPtr := @Self
  397.  
  398.  
  399.   TFrame
  400.     CloseButton:   PutEvent   evCommand,cmClose,InfoPtr := Owner
  401.     ZoomButton:    PutEvent   evCommand,cmZoom,InfoPtr := Owner
  402.     NextWindowBut: PutEvent   evCommand,cmNext,InfoPtr := Owner
  403.  
  404.  
  405.   TScrollBar
  406.     .HandleEvent  for evMouseDown or recognized evKeyDown, calls
  407.        Clicked (local procedure, not a method)
  408.          Message   
  409.              Owner,evBroadcast,cmScrollBarClicked,InfoPtr=@Self
  410.     .SetParams calls
  411.       .ScrollDraw
  412.          Message   
  413.              Owner,evBroadcast,cmScrollBarChanged,InfoPtr=@Self
  414.  
  415.  
  416.   TListViewer, TListBox
  417.     .HandleEvent
  418.       evBroadcast
  419.         cmScrollBarClicked
  420.           if command is from its own scrollbar
  421.             (checks InfoPtr versus HScrollBar,VScrollBar)
  422.           then calls TView.Select {ListViewer selects itself}
  423.         cmScrollBarChanged
  424.           if command is from its own scrollbar:
  425.             if from vertical scroll bar
  426.                 then .FocusItemNum(VScrollBar^.Value)
  427.             .DrawSelf {TVGraphic}  - which calls .Draw
  428.  
  429.     .ChangeBounds calls TScrollBar.SetStep DIRECTLY
  430.         SetStep then calls TScrollBar.SetParams    .SetRange     calls TScrollBar.SetParams DIRECTLY
  431.  
  432.  
  433.   TInputLine,TgInputLine
  434.     does not communicate with other views. 
  435.     Note that descendent TFileInputLine receives update commands
  436.       from a TFileListBox.
  437.    
  438.  
  439.   TLabel,TgLabel
  440.     Link : PView    selectable view that label is linked to.
  441.     .HandleEvent
  442.       If mouse clicks on label or hot key is pressed, calls
  443.       Link^.Select which causes linked view to select itself -
  444.       to become the selected view in the dialog.
  445.  
  446.       if Event.What = evBroadcast then
  447.         if (Event.Command = cmReceivedFocus) or
  448.          (Event.Command = cmReleasedFocus) then
  449.       Label sets its Light field true if its linked view is
  450.         the focused view. Then redraws itself. 
  451.         Label colors differ when Light is true.
  452.  
  453.