The Computer Guy

ACCEPT vs. LOOP

Clarion for Windows Tips
January 1996


This 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:

  1. Your program should present the user with information and let the user choose what to do.
  2. 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:

  1. 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).
  2. 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 solves 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.


HomeContact information
Copyright © The Computer Guy