236 ObjectPAL: disableDefault
VERSIONS: All ? - written for Paradox 5

SUBJECT: disableDefault

Comment A - BY: Randy Magruder - Borland

EventInfo.setErrorCode() is almost always the preferable way to block an event. The difference is somewhat internal but here it is in an example form. Lets say you have canDepart code. You use disableDefault. What this means is: "Whatever your default behavior is for canDepart, don't do it". In the case of canDepart, that's tantamount to saying "Don't ask permission to depart the field". DisableDefault therefore bypasses Paradox's logic which controls whether a field is given permission to be departed. If, however, you set an error code on the event, the built-in default code DOES execute. Any code WITHIN that default behavior which checks the errorcode will cause an internal clean-up and return from the canDepart process.

In almost all cases, the error code is the first thing that the default behavior code checks, and if its a non-zero value, it immediately returns. However, if you want to block the default behavior COMPLETELY, you can use disableDefault. The reason *I* wrote disableDefault in the code block I handed you is because I'm not sure at what point the error code is checked for every single dataAction, so I used the far more restrictive "disableDefault" than the somewhat liberal and forgiving "setErrorCode". If you have some time, you should try setErrorCode first and see whether it meets your needs. If not, use disableDefault. In most cases setErrorCode is what you want to try first, because it is a guaranteed SAFE method of letting Paradox know an error condition exists. With disableDefault, you deny Paradox the chance to do a safe cleanup.


Comment B - BY: Mark Pauker

Question:
Do you have any good examples of situations where disableDefault would be correct? I.e. where you would want to block the default behavior but still tell Paradox that the behavior has been handled without an error? So far I have been using setErrorCode exclusively and have not found any obvious need for disableDefault, but there must be some cases where it is needed.


Reply:
There are lots of situations where I'd use disableDefault instead of setErrorCode(). One very frequent use is to stop bubbling. For example, if I issue a "ui.action(UserAction)", the UserAction is going to be sent to the uiObject in question, and then bubble all the way up to the form. As soon as an object handles the action, though, I want the bubbling to stop, and I want the action to return True.

Without issuing either disableDefault or setErrorCode, the event would continue to bubble. This may be alright in some circumstances, but makes it nearly impossible to create reusable objects (since the actions will bubble up through other objects that shouldn't be seeing them, and those objects might have completely different meanings associated with the UserAction constants).

Issuing a setErrorCode in this situation should stop the bubbling, but would also cause the action to return False (which by convention means that the action didn't take place). As we stated earlier, though, the action _did_ take place.

Another example of where I use disableDefault is in conjunction with hotkeys. If someone presses a key that my application defines as a hotkey, my code deals with it. For example, let's say I defined the F12 key as a hotkey which exits edit mode. When that key gets pressed, I would issue an "active.action(DataEndEdit)". Since I have taken responsibility for the functionality of the keystroke, I want to disableDefault to allow the method to return True and yet not do what it would normally do in response to F12. (Actually, I would most likely check the value returned by the action; if True I would disableDefault, and if False I would issue a setErrorCode showing that the keystroke did not accomplish its task.)

As you can see, there is some leeway here in terms of how we define success and failure for a method. The place where people need to be careful is when dealing with actions and menuActions. As my last message pointed out, it doesn't make sense to issue a disableDefault in conjunction with a DataUnlockRecord because that would mean that _we_ had unlocked the data model's record, which is not possible. (Only Paradox's default behavior can do that.)

By using setErrorCode exclusively, you have avoided the more significant problem of telling Paradox that you've done something when in fact you haven't. If one were to err on either side, this would be the right way to go. That's why I tell people that they should use setErrorCode exclusively until they feel comfortable with the differences between them.

I find that once people grasp these concepts, Paradox seems several orders of magnitude less difficult.

< Quote >
In retrospect, it is kind of surprising that so much vague and even misleading information was circulated around this issue.
< /Quote >


I think the problem is that it's very difficult to factor out the simple rules which make up complex systems, and the Paradox manuals don't really describe the theory behind the event model at all. Thus the community was left to describle the event chain with rules based only on preconceptions and observations.

The problem is that most people's conclusions have been wrong. (I know that mine were for the first two or three go-'rounds.)

The reason why I am so emphatic when describing the difference between disableDefault and setErrorCode is that that understanding removes the mystery (and thus 95% of the complexity) of the event model. While I may not have memorized all of the RTL methods, and I certainly don't know the default behavior of every object in every situation, the fact is that I find Paradox a relatively straightforward environment to work in now.


Comment C - BY: Mark Pauker

< Quote >
if eventInfo.isPreFilter() then
else
      switch
            case eventInfo.id() = DataToggleEdit :
                  disableDefault
                  disableDefault
                  {any other things to be disabled}
      endswitch
endif
< /Quote >


While both disableDefault and eventInfo.setErrorCode(...) will prevent the toggling of the edit mode here, they will do it for different reasons. When you say disableDefault, you are saying: "I have done what needed to be done to successfully complete the current operation, so Paradox doesn't need to do anything further." In effect, you are telling Paradox that you have successfully toggled the edit mode. This is why when you issue:
       action(DataToggleEdit)
the method will return True (leading you to believe that edit mode _was_ toggled when in fact it wasn't).

Using eventInfo.setErrorCode(...) explicitly tells Paradox not to do the action it was asked to perform, thus Paradox knows that the edit toggle didn't take place and acts accordingly (returns False, among other things).

Even now, many people believe that there is a bug in Paradox because when they issue an action, Paradox returns True (telling them that the action completed successfully) when in fact the action didn't complete at all. In reality, it is not Paradox, but rather they themselves who are causing this to occur. The value returned when one issues:
       action(DataPostRecord)
describes whether or not Paradox encountered a _problem_ when calling the built-in. It does not tell you whether or not the record was posted. A "problem" is defined as an error which occurs when attempting to do the default behavior. If you were to issue a disableDefault within the action method, then the default behavior would not be attempted, and thus no error could occur. In this case, as far as Paradox is concerned there would be no problem, and thus Paradox would return True.

On the other hand, setErrorCode explicitly tells paradox that an error occurred within the default behavior and thus would cause the method to return False. The offshoot of all of this is that if we use setErrorCode properly, then the return value we receive when we attempt to post a record really _will_ tell us whether or not the record was successfully posted.

The ramifications of using disableDefault incorrectly go beyond return values as well. Do you remember early on in v1.00 when a record sometimes remained locked after the user moved off of it? This Paradox bug would leave records locked and give you no way of unlocking them without exiting Paradox. Turns out, this was not a bug at all. What was happening was that developers were using disableDefault when attempting to stop the records from being unlocked. Unfortunately, what they were in fact telling Paradox was that _they_ had unlocked the record so Paradox didn't have to bother. Paradox took them at their word and moved off of the record assuming that it had been successfully unlocked. (Paradox was young then... it didn't understand that people were going to try to mislead it. ) The developers should have been using setErrorCode().

Because the ramifications of the problem were so severe, and because most people perceived this as a bug in Paradox, Borland quickly "fixed" this by placing some extra checks to make sure that no matter what we told it to do, Paradox would not leave a record object while it was still locked. If you use the tracer to look at what happens when disabling the DataUnlockRecord using both techniques, you will see how clean and efficient it is when using setErrorCode(), and how confusing and inefficient it is when using disableDefault.

I think of the above example often when deciding whether to use setErrorCode or disableDefault. The fact is that in many circumstances, they can be used interchangeably without effecting the functionality of an app. The problem remains, though, that only one of them is strictly correct in any given circumstance, and if we "lie" to Paradox, there's always a chance that something unexpected might occur (like the records being left locked in v1).

To index