The Farber Consulting Group, Inc.

Tel: (516) 796-6545 / Fax: (516) 796-1273
E-Mail Me:
doron@dfarber.com
Our Web Site: http://www.dfarber.com

 

Prevent OKL Recursion Every Time

 

This following article was published in FoxTalk on June 1994

 

Uncontrolled ON KEY LABEL (OKL) handler recursion is one of the most common problems with using OKLs assignments. This is
caused by the user holding down a key so that it repeats continuously, resulting in the dreaded error 103, "Do Nesting Too Deep."
This error is actually more prevalent on slower hardware but can happen on faster equipment as well depending ran the user’s habits,
I have found a way to prevent such recursions.dependably

Here's an example. I have a general procedure called GoRecNo that handles all record movement in my applications. I use OKLs to
define the shortcut keys for the record movement buttons—previous, next, last and first - because I use control keys such as Ctrl-N
and Ctrl-P as shortcuts for those buttons. This rules out using hot key definitions in the button prompt strings.

Some of my users want to be able to hold these shortcut keys down continuously , then release the key when they reach the record.
When they tried this, they were getting the recursion problem. The first keystroke called my GoRecNo procedure, and sometime
before GoRecNo was finished executing the second keystroke woke up another call to GoRecNo, and so on. The way FoxPro handles
this internally, two DO levels are actually used up for each keystroke, and you quickly reach FoxPro's limit for nested program calls.

Interestingly, this was happening even though I had performed a PUSH KEY CLEAR at the start of GoRecNo (which in theory should
disable all ON KEY LABEL assignments), and then did a POP KEY at the end of the routine to restore the OKL assignments. The reason
is that OKL events can and do occur between the time of the initial keypress and the moment PUSH KEY CLEAR is executed, even if it's
the first statement in your handler.

The solution proved to be simple and fairly clean. When you start FoxPro, SET TYPEAHEAD defaults to 20, meaning that the user can type
up to 20 characters ahead. Many developers have this set at the maximum setting of 128. However, if you SET TYPEAHEAD TO 0 as the
first order of business in your OKL handler, you prevent further typeahead and therefore prevent OKL recursion. I found that although this
prevents recursion, I would still get one unwanted repeat when I released the key I was holding down. I found that CLEAR TYPEAHEAD
solved this problem.

Here is a model for a recursion-proof OKL handler

PROCEDURE OKhRandler
PARAMETER cAction

SET TYPEAHEAD TO 0
PUSH KEY CLEAR

* Call the appropriate service routine.

&cAction
CLEAR TYPEAHEAD

SET TYPEAHEAD TO 128
POP KEY
RETURN

 


Page 2

 

If an defined OKLs go through this handler, you avoid having to add this "recursion proofing code in every OKL handler you write.
Here's a sample OKL setup usurp this routine:

ON KEY LABEL CTRL+P DO OKLHandler WITH [DO GoRecNo WITH 'Previous']

OKL Handler is very generic and simplistic, but it can be adapted or optimized in many ways. For instance, if none of your
OKLs need to have arguments passed to them, you could change the macro expansion, &cAction, to DO (cAction) and
modify the ON KEY LABEL calls accordingly.

You could replace &cAction with a CASE statement that processes the action specified in cAction; this would allow more
flexibility in how the handler is called. Or, if your application uses a central event handler attached to your Foundation READ,
you can make just the "Recursion proofing" code part of the CASE in your event handler that processes key events.

Here is a model for an event handler for a recusrion-proof OKL
DO WHILE m.Terminate
 DO CASE
   CASE m.MenuEvnt
    Your Code .........
   CASE m.HotKeyEvnt
     SET TYPEAHEAD TO 0
     PUSH KEY CLEAR
     IF m.NowKeyEvnt==""
       DO (m.NowProc)
     ELSE
       DO (m.NowProc) WITH m.NowKeyEvnt
     ENDIF
     CLEAR TYPEAHEAD
     SET TYPEAHEAD TO 20
     POP KEY
   CASE m.ButtEvnt
     Your Code .........
   OTHERWISE
     READ VALID RetVal()
  ENDCASE
ENDDO
That’s how I implemented it in my own work. 

Actually, I find that PUSH/POP KEY statements don't even seem necessary with this scheme, but I leave them in "Just in Case"
FoxPro (and users) can be unpredictable at time. The placement of CLEAR TYPEAHEAD after performing your OKL actions is
important; it eats any keystrokes the user may input during the execution of the routine and which would then be released by
resetting the TYPEAHEAD buffer to a non-zero value.

Also, you should adjust the SET TYPEAHEAD TO 128 statement to whatever TYPEAHEAD setting you use in your applications.