Difference 2: What happens when a processing function is called.

To examine this difference between modal and non-modal programming, consider an example that is slightly more complex than the one mentioned above. In this example, you have a menu item on your top-level menu that brings up a data form. While the user is in the data form, he or she can press a key that brings up a stand-alone choice list, or another key that brings up a second form.

In modal programming, each panel (a panel is a form, menu or window) is processed by calling a processing function for that type of panel. A form is processed by calling fm_proc() or fm_rd(); a menu is processed by calling mn_proc() or mn_rd(); a choice list, by calling cl_proc() or cl_rd(). In modal programming, when this processing function is called, it gets control and keeps control until the user has decided to quit or exit from the panel being processed. In our example above, the programmer calls mn_proc() to process the top-level menu. The user selects the menu item that brings up the data form, and fm_proc() is called to process that form. The original call to mn_proc() is still active, but fm_proc() has control and is processing the data form. If the user presses the key to bring up the choice list, cl_proc() is called to process the choice list. When the user exits the choice list, cl_proc() returns and fm_proc() gets control again. When the user exits the form, fm_proc() returns and mn_proc() continues processing the menu. This is the same way that your code works when you call a function from within another function. The calling function is still around on the stack; when the called function exits, the calling function regains control.

By contrast, in non-modal programming, only one processing function is used to process all the panels regardless of whether they are forms, menus or windows. Each of the processing functions is now able to process any non-modal object. Thus, in non-modal programming, mn_proc() can process a data form or a stand-alone choice list; fm_proc() can process menus and choice lists, and so on.

Let's go back to the example above. If the menu, data form, and stand-alone choice list are all non-modal, then the user can move freely back and forth among them. He can, for example, select the data form, fill out a few fields on it, and then select another item from the menu without completing the data entry form. He did not exit or quit from the form; he is simply temporarily leaving it to do something else.

If Vermont Views started a new processing function each time the user moved into a panel, the end result would be that there could be many copies of each processing function active at the same time. For example, when the user enters the menu, mn_proc() is called. If the user selects the menu item to process a form, fm_proc() is called. If the user goes back up to the menu to select another item, we don't really want to call mn_proc() again, because there is already a copy of mn_proc() in memory, and it is already processing the menu. However, the user did not necessarily quit or exit from the form, and so fm_proc() cannot simply return.

To deal with this problem, Vermont Views has added the concept of suspending processing. When the user moves back into that menu, he does not exit the form, he simply suspends it temporarily. When he moves back into the form later, he will be in the same field as when he left (unless he has deliberately moved to another field), and the form will be in the same, partially-completed state.

In order to implement this idea of suspending processing, Vermont Views has had to change the way the processing functions, such as mn_proc() and fm_proc(), work. When you are dealing with non-modal objects, one processing function now does all the work of processing all non-modal objects. You call a processing function for the first non-modal panel; this processing function then processes all other non-modal panels until there are no more non-modal panels on the screen. In the example mentioned above, mn_proc() or mn_rd() is used to process the top-level menu, and this is the first processing function called. When the user processes the data form or the choice list, the calls to fm_proc() and cl_proc() simply return, and mn_proc() is the function that actually performs the work of processing both the form and the choice list.

How Form Processing Works for Non-Modal Panels

When a processing function is called for a non-modal panel, the processing function first checks to see whether another processing function is active. If so, this second processing function does only two things: it puts the panel on the screen, if it isn't already there, and then it posts an event (for detailed information on posting events, see Chapter 37, "How Event Processing Works"). Two pieces of information are contained in the event structure that gets posted: an action code that tells Vermont Views to suspend processing on the current panel, and a pointer to the panel to process next (which is, in fact, the panel that this second processing function was supposed to process). The second processing function then returns, with a return value of AC_FMSUSPEND or AC_WNSUSPEND to indicate that it has posted an event to suspend the current panel.

After the second processing function has returned, the original processing function gets control again. As it is processing events from the event queue, it finds the event posted by the second processing function. Vermont Views then suspends processing of the current panel and starts processing the new panel. The original processing function is still active, but it has switched its focus of attention to the new panel. It will continue processing the new panel until the user quits, exits, or suspends that panel; then it will shift its focus to a different panel. The original processing function will not return until there are no more panels on the screen for it to focus on.

Your call to the second processing function, then, will return before the user gets a chance to enter data or make selections in the panel. In fact, your event function itself, whether it be a menu action function, a begin-field function, or any other type of function, will return before the user can enter the panel you specified. At the time that your processing function returns, Vermont Views has not allowed the user to enter that panel, type data, make selections, or perform any other action with that panel.

What This Means to You

This has several implications for the way you write your application. First, if you call a processing function (such as fm_proc(), mn_proc(), wn_proc(), cl_proc(), etc.) on a non-modal panel, you must not take the panel off the screen or free it immediately after the call to the processing function. The panel must remain in memory and on the screen until Vermont Views has given the user a chance to enter the form and perform whatever actions are available to him, such as entering data, making menu selections, and so on.

Second, if you call fm_proc() on a non-modal form or cl_proc() on a non-modal stand-alone choice list, you cannot access the data that the user entered immediately after the call to the processing function. The processing function posted an event and returned immediately; the user hasn't had a chance to enter data or make a selection yet.

Third, a function that processes a non-modal panel cannot allocate memory related to that panel off the stack; the memory for things such as form data and user pointers must be allocated from the heap or declared as static or global variables. Your user function will return before the user gets the chance to type data into the form, and stack variables are freed when the function that declared them returns. If you try to store the form data in stack variables, you will have a memory overwrite in your program as Vermont Views tries to store form data in an area of the stack that has been freed.

Fourth, the return value from a call to a processing function that processes a non-modal panel is almost worthless. All processing functions except the original one will return a value of AC_FMSUSPEND, which is useless. The original processing function will return a standard value such as AC_EXIT or AC_QUIT. However, this return value will apply to the last panel that was processed, and that may not be the same panel that you specified in the call to the processing function. Keep in mind that when the user exits one panel, Vermont Views tries to move him into another panel on the screen. Only when there are no more panels on the screen will the original processing function return. You then have no information about what panel this return value applies to, and the application has no way of determining which panel was the last panel processed. Therefore, you cannot check return values to determine whether the user exited or aborted from a panel.


Home Contents Previous Next