home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 21
/
CD_ASCQ_21_040595.iso
/
dos
/
prg
/
pas
/
tvgr70
/
beginner.doc
< prev
next >
Wrap
Text File
|
1995-01-08
|
34KB
|
977 lines
11/22/94
Beginner's Turbo Vision/TVGraphic Tutorial
This file attempts to give a brief once over of areas that trou-
ble Pascal programmers when working with Turbo Vision and
TVGraphic. (TV is used when referring to both.)
Contents
READ THIS BOOK
Square One - Event Driven
The Transition to TV
Basics on Pointers in TV
Safety when constructing objects
Destructing Objects - Dispose versus Done
PStrings and Memory Overwrites
TV/TVGraphic - Order of Views
Inserting Views - Assumptions, Where, How
DrawView versus Draw
Color Palettes
Communication Among Views in a Dialog Box (TDialog)
READ THIS BOOK
Finally a book that really clarifies how to use TV. Starts right
from the beginning. Goes through the complex. Not just a copy of
the words in the Borland documentation. Published June 1994.
You want: Programming with Turbo Vision
B. Watson
M&T Books
ISBN 1-55851-399-X
Square One
A TV application consists of
begin
MyApplication.Init
MyApplication.Run
MyApplication.Done
end. - that's all.
So except for initialization and clean up, all the code executes
within Run. Run calls MyApplication.GetEvent and then MyApplica-
tion.HandleEvent repeatedly in a loop. (This loop only terminates
when the program ends.) GetEvent is often inherited from TProgram
and used as is. It fetches Events: keystrokes, mouse actions and
events (referred to as Commands) generated by Menus, Dialog box
buttons, etc. The Event is then passed to MyApplication.HandleE-
vent.
MyApplication.HandleEvent calls TProgram.HandleEvent which then
calls the HandleEvents for all the views inserted directly or
1
indirectly into MyApplication. If the Event is from the mouse, it
is passed to the view the mouse cursor is within. Otherwise
events are usually passed to the "selected" view. That is the
view that is the focus of the user's (and the program's) atten-
tion at this moment. (Views = objects visible on the screen :
Menus, DeskTop, Windows, etc. Inserting a view into the DeskTop,
Window, Dialog, etc. makes the view visible and causes Events to
be passed to the view's HandleEvent.).
Your job is to write the HandleEvents for the views you create
and for standard views you want to modify. Within these HandleE-
vents, you select which Events to respond to and then call your
code written for that Event. Each block of code you want to
execute will be triggered directly or indirectly by an Event.
Note that your HandleEvent must identify and then respond to at
least one type of Event to have any effect on the program. Also
note that you almost always call the HandleEvent of your view's
ancestor object somewhere in your HandleEvent. The call to the
ancestor's HandleEvent calls the ancestor's code/behavior.
This is called Event Driven programming. If no events are gener-
ated by the user or by your code, then TV will sit idle. Forever.
A consequence of Event Driven programming is that the sequence in
which the program executes is not as rigidly defined as in con-
ventional programming style. Also you will most often issue a
Command Event to call code in one view from another, rather than
calling the code directly. It will take a while for this to seem
natural.
HandleEvents of standard views make standard calls to various
methods. So you can also modify a view's behavior just by writing
a new or modified version of the called method. This is often
simpler than modifying HandleEvent.
The Transition to TV
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 envi-
ronments and pointers are in Borland's doc but thinking Turbo
Vision "pointer" isn't. That's here.
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 so-
phisticated and flexible. Skim the Borland manuals on Collec-
tions, Streams and Resources. These are very powerful data han-
dling 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.
2
BASICS ON POINTERS IN TV
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 by using pointers to objects can you write code that works
with an object type the user selects at run time! And 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.
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.
Let's look at ways of thinking that may help.
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.
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 Type in that it contains data
fields but it also defines Functions and Procedures which belong
to the object type. Next there are 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.
3
Turbo Vision speak:
1. Assume in TV that there is a pointer type defined for every
object type. (Follow this practice yourself.)
TView - T indicates an object type (a class)
PView - P indicates a pointer type. PView type pointers point
to TView type objects (and their descendants).
example
type {Pascal allows this out of order declaration
within a single Type statement}
PView = ^TView
TView = Object
Owner : PGroup;
Size : TPoint; {TPoint is a record with fields
x,y}
...
...
end;
! NOTE THE ^ SYMBOL. It is Critical to Understanding !
With pointers and variables (and their types), the ^ lets
you grab one if you know the name of the other.
^Variable is a pointer to Variable
Pointer^ is the variable that Pointer points to.
Think about this for a minute.
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.
example
var
MyView : PView;
x : integer;
begin
MyView := New(PView, Init(.......));
x := MyView^.Size.x;
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.
Turbo Vision's TView has a field Size which contains a record
with x and y fields.
So "Size.x" by itself should look familiar.
What may not look familiar is MyView^ .
4
And how about MyView^.Owner^.Size.x ?
Welcome to Turbo Vision and the land of pointer.
Take a deep breath. Exhale.
Ingest your favorite mood altering substances.
Say: It will all make sense.
We said MyView^ meant the variable (object) pointed at by MyView
so MyView^.Size
says that Size is a field in variable (object) MyView^.
Remember MyView^ is an object of type TView.
(Think of the object as a Record with various fields)
In TV, each TView has a field Owner which is of type PGroup.
TGroups are descended from TViews.
then MyView^.Owner^.Size
will be the Size field of the Owner^ (TV jargon) of MyView^.
That is, Owner is a PView pointer. So Owner^ is another
object of a type descended from TView.
It therefore has ITS OWN Size field!
THAT Size field is: MyView^.Owner^.Size !
3. See > MyView^.Owner^.Size
Think > MyView's Owner's Size
Since Size is just a record type variable, the x field of
Size is as usual - Size.x
Again, MyView and Owner are the pointers, MyView^ and Owner^
are the actual objects.
If you can start to think as in 3 above, you are over the hardest
part.
Take a Break
SAFETY WHEN CONSTRUCTING OBJECTS
HOW TO CRASH YOUR COMPUTER, two ways, no sweat
MyView^.Owner^.Size
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.
5
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.
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
Owner^.MoveTo
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.
>>> Calls to virtual methods of uninitialized objects can be
trapped as a runtime error if the compiler's Range checking is
turned on. However no errors will be found if there are no virtu-
al methods for the object.
The other common way to crash also comes from pointer fields in
objects. Say you sometimes create a TCollection dynamically using
NEW and assign it to the pointer MyView.CollectionPtr but some-
times you do 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.
The only real protection is to be methodical about testing wheth-
er pointers are set to nil before calling "Pointer^.procedure" or
disposing pointers.
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 test-
ing whether the pointer is still nil. Turn RangeChecking on for
the first YEAR.
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.
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?
6
5. Pointer Safety in Construction
{note: a TGroup is a descendent of TView}
var
MyGroup : PView; {can point to any descendent of TView}
begin
.....
.....
> MyGroup := New(PGroup, Init(.......)); <
!!! ALWAYS do Construction as above !!!
Using the NEW function in the special way above combines allocat-
ing the memory for a new object with calling its constructor Init
to initialize the object and build its virtual method table.
Yes there are other ways.
New(MyGroup, Init(.........));
Problem : Assuming you wanted a TGroup, you just constructed
and initialized a TView since MyGroup is of type TView. What-
ever type pointer MyGroup is will determine what type of object
you will construct. If the variable declarations are not on
screen, it is easy to forget what type MyGroup really is.
MyGroup := New(PGroup);
MyGroup^.Init(........);
NEVER! if you forget to call .Init before you call a method of
MyGroup, could bomb - Reset switch time.
Aside - There must be a way around all these pointers.
Now it has occurred to some of you (it did to me) that one can
avoid some of the pointer experience when using standalone ob-
jects by doing
var
MyObj : TView; {TView is object, NOT a pointer type}
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.
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.
Break
7
Easy stuff
How about the ^Variable usage?
In Turbo Vision it is common to organize your variables (objects)
by creating either a linked list of pointers to the variables or
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.
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.
The ^Variable usage is much less common than the Pointer^ usage.
This is simply because most object variables are already pointers
in TV.
APointer := ^AnyVariable;
assigns the address of AnyVariable to APointer.
Also you see this all the time in type declarations:
type
PMyView = ^TMyView;
Which declares PMyView to be a pointer to type TMyView.
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:
type
Str20 = string[20]; definition
PString = ^String; definition
procedure DoIt(StrPtr : PString); definition
begin
end;
....
Str20 := 'AARDVARK';
DoIt(Str20); {type mismatch error}
DoIt(^Str20); {simple as that}
If you have stuck it out this far, congratulations. Class dis-
missed.
{-------------------------------}
8
DESTRUCTING OBJECTS - Dispose versus Done
TV documentation mentions that you should always call Done when
you are finished with an object. If the object is a TGroup, then
Done calls Done for all the subviews AND frees their memory. But
Done does not free the memory used by the TGroup itself. If you
have declared the TGroup dynamically on the heap (New(PGroup,
Init...)), then its memory is not freed/recovered.
Dispose(PMyObject, Done) both calls the Done destructor and
frees/recovers the memory used by MyObject.
A simple rule is:
Match each call to New(PMyObject,...)
with a call to Dispose(PMyObject, Done);
If you declare the object as a static variable
var MyObject : TMyObject; {not PMyObject}
then just call Done. The compiler allocates and frees memory for
static variables.
A suggestion:
It is easy to forget to recover memory. Take a copy of the small
demo program TinyDemo and add the HeapViewer from the demo
TVGDemo1. Then test your code using this program. The HeapViewer
will tell you immediately if you are losing memory.
{-------------------------------}
PSTRINGS and MEMORY OVERWRITES
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:
function NewStr(S : string) : PString;
So AStrPointer := NewStr('a string')
allocates memory for the string "a string" and returns a point-
er 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.
This is fine until you want to change the contents of the string.
If you create a regular Pascal string
MyString : string = 'Longer than a string';
and then assign MyString to the string pointed to by AStrPointer
AStrPointer^ := MyString;
you Will OverWrite Memory not belonging to AStrPointer! MyString
9
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.
There are no error messages or compiler checks.
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.)
>> Think twice before changing the contents/length of a string
pointer.
{-------------------------------}
TV/TVGraphic ORDER OF VIEWS
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.
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) con-
tains 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.
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.
Terminology
Z- order : partially defined concept in Borland doc to explain
view order
function TGroup.First
Borland = "returns a pointer to the TGroup's first subview, the
one closest to the top in Z-order."
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 in-
serted using TGroup.Insert becomes the First subview.
function TView.NextView
Returns a pointer to the "Next" subview in the view's Owner. So
10
if you start with the First subview. You can chain through all
the subview NextView pointers to reach the Last subview.
function TView.PrevView
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.
TGroup.Last pointer field
Points to the Last subview in order. This view receives events
last. Draws first in TVGraphic. Draws last in Turbo Vision.
Top
When discussing Z order, the First subview is at the top.
Bottom
When discussing Z order, the Last subview is at the bottom.
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 sub-
view.
{----------------------------}
INSERTING VIEWS - Assumptions, Where, How
TV is based around certain assumptions about how you will organ-
ize your program. You don't have to follow these assumptions but
it sure makes life easier.
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.
The second assumption is that you will insert all windows and
dialogs into the DeskTop. (InsertWindow and ExecuteDialog do this
automatically for you).
The third assumption is that you will insert all objects defined
in unit Dialogs that are selectable or cause other objects to be
selected (buttons, inputlines, listboxes, labels, etc.) into a
dialog.
Well, what if you don't want to?
First Assumption
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 TPan-
Window in main doc file.
Also inserting views outside the above assumptions will usually
cause them to draw in different colors since they map into a
11
different part of the color palette. For example. a dialog's
palette has many more entries than a TWindow, including entries
to TButtons. In Turbo Vision, if you insert a button into your
descendant of TWindow, there are no TWindow palette entries
defined for it. In TVGraphic, the window palettes have been
extended so the colors will be correct. However the buttons'
behavior will be different since a window and dialog HandleEvents
differ.
Second Assumption
Note : Don't try to insert a dialog into a dialog.
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.
Non-modal dialogs (persistent dialogs}
Insert into the DeskTop (DeskTop^.Insert). TVGraphic also allows
insertion of non-modal dialogs into a TPanWindow.
In General
In TVGraphic (and perhaps Turbo Vision): If you insert a non-
modal view into the Application rather than the DeskTop AND this
view overlaps the DeskTop, the redrawing mechanism will not work
properly. It can't tell in what order this view should be drawn
relative to the DeskTop views.
Third Assumption
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 Assump-
tion.
{----------------------------}
DRAWVIEW vs DRAW
Even though Draw is the method that actually draws the view, you
should always call DrawView when you want a view to draw itself.
DrawView checks if the view is Exposed and also resets the text
cursor if one exists.
{----------------------------}
COLOR PALETTES
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.
Note: It is often easier to avoid using the palette when writing
new Draw methods. Work back toward the palette method of obtain-
ing color as your application firms up.
12
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.
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.
As GetColor works it way up the view chain, it calls GetPalette
at each level and sums the offset value from each palette entry
to get the total offset into the Application palette. (The Desk-
top GetPalette returns nil - it doesn't have a palette so the
offset values GetColor has calculated are not changed by the
DeskTop.)
Pragmatically:
TV1.0 Dialogs:
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.
(A subview palette offset of 1 will map to the first dialog
palette entry of 32. So 32-1 = 31.)
TV2.0 default "Cyan" Dialog
The offset of the first palette entry is 96. Anything inserted
into a Cyan dialog will have the offsets of its own palette in-
creased by 91.
For a "BlueWindow"
The offset in the first palette entry is 8. So anything insert-
ed into a "BlueWindow" (light gray with blue highlights in
TVGraphic) will have the offsets in its own palette increased by
7.
If you insert a window in a window for example, then entries will
be increased by 7 by each window's GetPalette. The inserted
window no longer maps to the expected colors. TVGraphic's TSubwin-
dow avoids this problem by using a modified GetPalette function
which senses when the subwindow's Owner is also a window or
descendant of TWindow.
TVGraphic handles the insertion of dialogs in a window by extend-
ing the window palette.
13
Example:
TInputLine inserted into the usual Cyan Dialog
The second entry in the TInputLine palette is for "Active"
text. (Active means the window TInputLine is inserted into
is in the Active state.)
The offset of the second entry is 19.
Draw obtains the active colors by calling GetColor(2).
(2 for second entry in TInputLine's palette)
To calculate the offset into the application's Palette,
add 91 (from cyan Dialog) to 19 = 110. The colors GetColor
returns are from the 110th entry in TApplication's palette.
{----------------------------}
COMMUNICATION AMONG VIEWS IN A DIALOG BOX (TDIALOG)
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.
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.
Note that when a Dialog box is modal, there is no real differ-
ence in a subView using the Message function to call its Owner
and sending a PutEvent - both events end up in TDialog.HandleE-
vent. (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.
BUT if a Dialog box is Not Modal, an event sent with PutEvent
would first show up at TProgram.HandleEvent. If it was an ev-
Broadcast, it would be received by EVERY view in the application!
Note that EndModal returns the terminating command through
ExecView or ExecuteDialog.
Summary of commands and messages:
TDialog.HandleEvent(Event)
if AllowBothMButtons is false (TVGraphic default) then
right mouse click generates a PutEvent with
Event.What := evCommand
Event.Command := cmCancel
Event.InfoPtr := nil
call TWindow.HandleEvent, return
keystrokes:
kbEsc: PutEvent evCommand,cmCancel,InfoPtr := nil;
14
kbEnter: PutEvent evBroadcast,cmDefault,InfoPtr := nil;
evCommand:
case Event.Command of
cmOk, cmCancel, cmYes, cmNo:
if State and sfModal <> 0 {if TDialog is modal}
EndModal(Event.Command);
{next part is TVGraphic only}
else if (VOptions and ReturnOnAnyButton <> 0)
and (State and SfModal <> 0)
EndModal(Event.Command);
end;
TButton
Note: TVGraphic handles the Default button differently from TV
TV buttons send messages using commands
cmGrabDefault, cmReleaseDefault.
TVGraphic does not use these messages or commands.
.HandleEvent
various situations call Press
.Press
Message Owner,evBroadcast,cmRecordHistory,InfoPtr=nil
if bfBroadcast set
Message Owner,evBroadcast,buttoncommand,InfoPtr= @Self
bfBroadcast not set
PutEvent evCommand,buttoncommand,InfoPtr := @Self
TFrame
CloseButton: if window has a pulldown menu, activate menu
and PutEvent the selected menu command,
otherwise PutEvent evCommand,cmClose,InfoPtr := Owner
ZoomButton: PutEvent evCommand,cmZoom,InfoPtr := Owner
NextWindowBut: PutEvent evCommand,cmNext,InfoPtr := Owner
TScrollBar
.HandleEvent for evMouseDown or recognized evKeyDown, calls
Clicked (local procedure, not a method)
Message
Owner,evBroadcast,cmScrollBarClicked,InfoPtr=@Self
.SetParams calls
.ScrollDraw
Message
Owner,evBroadcast,cmScrollBarChanged,InfoPtr=@Self
TListViewer, TListBox
.HandleEvent
evBroadcast
cmScrollBarClicked
if command is from its own scrollbar
(checks InfoPtr versus HScrollBar,VScrollBar)
then calls TView.Select {ListViewer selects itself}
cmScrollBarChanged
15
if command is from its own scrollbar:
if from vertical scroll bar
then .FocusItemNum(VScrollBar^.Value)
sets flag for later redaw of self {TVGraphic}
.ChangeBounds calls TScrollBar.SetStep DIRECTLY
SetStep then calls TScrollBar.SetParams
.SetRange calls TScrollBar.SetParams DIRECTLY
TInputLine,TgInputLine
does not communicate with other views.
Note that descendent TFileInputLine receives update commands
from a TFileListBox.
TLabel,TgLabel
Link : PView selectable view that label is linked to.
.HandleEvent
If mouse clicks on label or hot key is pressed, calls
Link^.Select which causes linked view to select itself -
to become the selected view in the dialog.
if Event.What = evBroadcast then
if (Event.Command = cmReceivedFocus) or
(Event.Command = cmReleasedFocus) then
Label sets its Light field true if its linked view is
the focused view. Then redraws itself.
Label colors differ when Light is true.
16