ACCEPT vs. LOOP
Clarion for Windows Tips
January 1996
This month's tip looks at the CW ACCEPT loop and how
it makes your programs "Windows-friendly." We
will be looking at
The Windows Paradigm
Understanding the new ACCEPT loop is much easier if
you understand how it is intended to fit into the Windows
paradigm. The most basic tenet of Windows is that the
user should always be in control. This implies two
rules:
- Your program should present the user with
information and let the user choose what to do.
- You should never "lock up" the user's
machine while you do something.
If you follow these two rules, your application will
display "standard Windows behavior," which
makes users happy. If you break these two rules, your
users may be uncomfortable with the way your application
works. The ACCEPT loop makes obeying these rules
possible.
Event-Driven Programming
The first rule naturally lends itself to Event-Driven
Programming. The premise of event-driven programming
is that your application is an event processor.
When an event occurs, your application is notified what
happened and then chooses how to respond to that event.
For example, if you have a window open with an OK button
on it and the user presses the OK button, your
application is notified that the OK button was pressed.
You then decide how to respond to that event.
Evolution of the ACCEPT
Statement
ACCEPT in DOS
The DOS ACCEPT statement is a screen processor. When
it is time for the user to do something with a screen,
the ACCEPT statement is executed, passing program control
to a library routine that waits for the user to complete
a field or press an ALERTed key. Then the program
continues with its execution until it is ready for more
screen input, when it executes the ACCEPT statement
again.
DOS ACCEPT statements are usually wrapped up inside a
LOOP to handle screen processing. The code looks
something like this:
LOOP
CASE SELECTED()
!Pre-processing code for each field
END
ACCEPT
CASE ACCEPTED()
!Post-processing code for each field
END
END
If you look at the basic construction of this code,
there is a LOOP that the program stays in while it is
accepting screen input. When a field is selected, the
pre-processing code for that field is executed and then
the ACCEPT statement is called so that the user can do
something with the selected field. When the user
completes the field, the ACCEPT statement terminates and
the field post-processing code is executed. Once that is
completed the loop cycles and the process begins all over
again.
ACCEPT in Windows
The biggest difference you will see between the DOS
and Windows ACCEPT statements is that the Windows ACCEPT
statement is a loop. Here's what the code looks
like for a Windows ACCEPT loop:
ACCEPT
CASE EVENT()
!Non-field-specific event processing code
END
CASE FIELD()
CASE EVENT()
!Field-specific event processing code
END
END
END
Note that the LOOP statement is gone and the ACCEPT
statement forms a loop. Inside the loop are two CASE
blocks. The first CASE block handles non-field-specific
events. These are events that are not related to a
particular field on the window. Examples of these
non-field-specific events are timer, close down,
minimize, maximize, and restore events. The second CASE
block has an OF section for each field that could receive
an event. Inside of each field's OF section is another
CASE block that handles the events directed to that
particular field.
The entire ACCEPT loop forms an event-processing loop.
Program execution waits at the ACCEPT statement until an
event is received. When the event is received, program
execution resumes and the appropriate section of code in
the loop responds to the event. After the code inside the
loop is executed, the loop cycles and waits at the ACCEPT
statement until another event is received.
While your program is waiting at the ACCEPT statement
for an event, the library routines turn control over to
Windows so that it can receive events and dispatch them
to the appropriate programs. This only happens while
waiting at the ACCEPT statement.
Once again, for emphasis: While your program is
waiting at the ACCEPT statement, the library routines
have turned control over to Windows until an event for
your program occurs. If your program is not at an
ACCEPT statement, Windows is not processing events and
you have taken exclusive control of the system.
Handling Events
As long as we're here, this is a good time to talk
briefly about handling events. I said earlier that the
ACCEPT statement is an event handler. Looking at the code
fragment above, there is a non-field-specific event
handling section and a field-specific event handling
section. Everything you do should respond to an event.
Here is a more complete version of the ACCEPT loop:
ACCEPT
CASE EVENT()
OF EVENT:CloseWindow
OROF EVENT:CloseDown
IF RecordChanged
IF MESSAGE('Do you want to cancel without saving?', |
'Are you sure?',ICON:Question,BUTTON:Yes+BUTTON:No, |
BUTTON:No) = BUTTON:No
CYCLE
END
END
END
CASE FIELD()
OF ?Cus:FirstName
CASE EVENT()
OF EVENT:Accepted
MyWindow{PROP:Text} = CLIP(Cus:FirstName) & |
' - Customer Information'
END
OF ?OKButton
CASE EVENT()
OF EVENT:Accepted
DO SaveRecord
BREAK
END
OF ?CancelButton
CASE EVENT()
OF EVENT:Accepted
POST(EVENT:CloseWindow)
END
END
END
As you analyze it, you will see that this ACCEPT loop
responds to five different events. The first two events
(EVENT:CloseWindow and EVENT:CloseDown) are events that
occurs if the user closes the application or presses the
Escape key. When these events occur, the program looks to
see if the record has been changed and displays a warning
message. If the user decides not to close the window (by
pressing no) then the loop is CYCLEd, which prevents it
from exiting as it would normally do on either of these
two events.
The other three events processed are the accepted
event (EVENT:Accepted) for the Cus:FirstName field, the
OK button, and the cancel button. When EVENT:Accepted
occurs for the Cus:FirstName field, the window title bar
is updated. When the event is generated for the OK
button, a routine is called to save the record and then
the BREAK statement breaks out of the ACCEPT loop. When
the event is generated for the Cancel button,
EVENT:CloseWindow is posted to the window, which causes
the warning message at the EVENT:CloseWindow event to be
displayed.
This gives a very small illustration of how the ACCEPT
loop has become an event handler. This provides the
perfect method for implementing rule number one:
"Your program should present the user with
information and let the user choose what to do." You
just wait for the event and then act on it.
What's Wrong with LOOP?
Now it's time to deal with the second rule: "You
should never 'lock up' the user's machine while you do
something."
The quickest way to break this rule is to use a LOOP
that will process for an extended period of time.
Remember that when the program is waiting at an ACCEPT
statement, Windows is free to do the things it does. When
the program is not waiting at an ACCEPT statement,
Windows can't do anything until the program gets to an
ACCEPT statement and releases control of the system.
If your program enters a LOOP structure and processes
in the loop for a while, nothing can happen anywhere else
in Windows until the loop completes and the program makes
it to another ACCEPT statement. If you are generating a
report that takes 15 seconds in a LOOP, the user's system
will be locked up for 15 seconds while the report
processes. (There are exceptions, but you should assume
that this rule is always true.) This violates rule number
two because the user's machine appears "locked
up" even though it is processing.
You can (and should) still use a LOOP for very short
processes. For example, you can use a LOOP to quickly
read a few records from a data file without a noticeable
result for the user. If you expect the process to take
more than a fraction of a second, you should try to use
an ACCEPT statement instead of a LOOP if at all possible.
Using ACCEPT for a
Friendly LOOP
The ACCEPT statement can be used with the TIMER
attribute on a window to create the
"Windows-friendly" equivalent of a LOOP. A
timer generates a periodic event that is sent to your
ACCEPT loop. If you have a window with a one second timer
on it, the timer event (EVENT:Timer) will be posted to
your ACCEPT loop once every second. By placing the code
that would normally be in a LOOP in the CASE block for
the timer event, your code will be executed every time
the timer event occurs and then control will return to
the ACCEPT statement so that Windows can continue
working.
Here is a simple example. This code is a loop that
reads the records in a data file and prints a detail band
on a report for each record:
LOOP
NEXT(DataFile)
IF ERRORCODE() THEN BREAK.
PRINT(Rpt:Detail)
END
The equivalent code using an ACCEPT loop is:
WINDOW{PROP:Timer} = 50
ACCEPT
CASE EVENT()
OF EVENT:Timer
NEXT(DataFile)
IF ERRORCODE() THEN BREAK.
PRINT(Rpt:Detail)
END
END
Two important things happen as a result of this code:
- Your program execution continues predictably even
if the user moves focus to another program. The
timer event will still be posted regularly (in
this case twice per second).
- Because you hit an ACCEPT statement every time
through the loop, you will be hitting the ACCEPT
statement regularly and cooperating with Windows
so that it can do its job.
It almost seems anti-climactic that after all that
build-up a simple ACCEPT loop solve the problem. Because
it is central to the operation of your Windows program,
the ACCEPT loop will be a common place for you to go when
adding functionality to your program.
As an example, assume that you have a program for
editing files. When the users chooses a file to open, you
open a new MDI child window to edit the file. If the user
tries to open a file twice, you probably want the first
window to be opened rather than opening two windows. To
accomplish this, you post an event to the ACCEPT loop for
the first window (remember that an MDI program is
multi-threaded) and then the event processing code for
that event can make your first window the active window.
This kind of flexibility in the ACCEPT loop allows you
to take greater advantage of event-driven programming,
making your programs more Windows-friendly and giving you
greater control over the way they operate.
Copyright © 1997-1999 - The Computer Guy - steve@compguy.com
Last updated Thursday, December 16, 1999
|