The problem
Often in code Visual FoxPro developers will find themselves having to save the previous state of something in order to be able to restore it once some code has run. The usual culprits are:
- SET command (such as SET SAFETY, SET DELETED, etc.)
- ON command (ON ERROR, ON ESCAPE, etc.)
- Record Pointer position
- Selected Work Area
The solution
Rather than writing the same lines of code over and over again in our programs, we could create a class that will handle this. The following is a SettingHandler class I've written and some examples of use. You'll note that when I instantiate the class I send it the command that I'm about to execute (such as SET SAFETY OFF) as a string parameter. The class will save the current Safety setting for me and then set it to OFF. When I want the previous Safety setting restored, I release the instance of SettingHandler and the class will restore Safety to whatever it was prior to my setting it off.
Drawbacks and possible improvements
The only drawback I've found to this approach is that intellisense doesn't work when I am typing in the command as a string parameter. As for improvements, most of the ones I can think of would have to do with table operations. Code could be added to allow you to save the current datasession id and restore to that datasession (there are probably other settings that could be provided for as well - Captions, ForeColor, BackColor, Tag, or whatever). Also, some logic could be added to handle if a developer switched work areas, but hadn't specified the work area in the GOTO command.
Here's a runnable example and class definition (Cut-n-paste the code below into a prg file and execute it)
*************************
*!* EXAMPLES OF USE
*************************
CLEAR
LOCAL loSetting
? "SET COMMANDS:"
SET SAFETY ON
?SET("SAFETY")
loSetting = CREATEOBJECT("SettingHandler", "SET SAFETY OFF") && Safety setting is saved and changed
?SET("SAFETY")
RELEASE loSetting && Previous Safety setting is restored
?SET("SAFETY")
?
? "ON COMMANDS:"
ON ERROR ?"Previous Error Handler"
?ON("ERROR")
loSetting = CREATEOBJECT("SettingHandler", "ON ERROR ?'New Error Handler'") && Error handler is saved and changed
?ON("ERROR")
RELEASE loSetting && Previous Error handler is restored
?ON("ERROR")
?
? "RECORD NUMBER:"
IF !USED("customers")
USE (HOME(2) + "northwind\customers.dbf") IN 0 SHARED
ENDIF
GOTO 5 IN "customers"
?RECNO("customers")
loSetting = CREATEOBJECT("SettingHandler", "GO 12 in [customers]") && record pointer position is saved and changed
?RECNO("customers")
RELEASE loSetting && record pointer position is restored
?RECNO("customers")
?
? "SELECT:"
IF !USED("orders")
USE (HOME(2) + "northwind\orders.dbf") IN 0 SHARED
ENDIF
SELECT "Orders"
?ALIAS()
loSetting = CREATEOBJECT("SettingHandler", "Select Customers") && Selected Alias is saved and changed
?ALIAS()
RELEASE loSetting && Previous selected Alias is restored
?ALIAS()
USE IN SELECT("Customers")
USE IN SELECT("Orders")
*********End of Examples************
************************************
*!* CLASS DEFINITION
************************************
DEFINE CLASS SettingHandler as custom
PROTECTED PreviousValue
PreviousValue = .NULL.
PROTECTED SettingCommand
SettingCommand = ""
PROTECTED SettingType && 0 = SET/ON, 1 = RECNO
SettingType = -1
#DEFINE SETTINGDELIMITERS [('" ] + "[])"
PROCEDURE Init (tcCommand)
This.Setup(tcCommand)
ENDPROC
PROTECTED PROCEDURE Destroy
This.RevertSetting()
ENDPROC
PROCEDURE Setup (tcCommand)
This.SettingCommand = ALLTRIM(tcCommand)
This.SaveSetting()
This.UpdateSetting()
ENDPROC
PROTECTED PROCEDURE SaveSetting
LOCAL lcFirstPart, lcSecondPart, lnSecondPosition, lcCommand
lcFirstPart = UPPER(ALLTRIM(GETWORDNUM(this.SettingCommand, 1, SETTINGDELIMITERS)))
DO Case
CASE INLIST(lcFirstPart, "SET", "ON")
lcSecondPart = UPPER(ALLTRIM(GETWORDNUM(this.SettingCommand, 2, SETTINGDELIMITERS)))
lcCommand = lcFirstPart + [("] + lcSecondPart + [")]
This.SettingType = 0
CASE INLIST(lcFirstPart, "GOTO", "GO")
lnSecondPosition = ATC(" IN ", this.SettingCommand)
IF lnSecondPosition > 0
lcSecondPart = SUBSTR(this.settingcommand, lnSecondPosition + 4)
ELSE
lcSecondPart = ""
ENDIF
lcCommand = [RECNO(] + lcSecondPart + [)]
This.SettingType = 1
CASE lcFirstPart = "SELECT"
lcSecondPart = UPPER(ALLTRIM(GETWORDNUM(this.SettingCommand, 2, SETTINGDELIMITERS)))
lcCommand = [ALIAS()]
This.SettingType = 2
ENDCASE
IF !EMPTY(lcCommand)
This.PreviousValue = EVALUATE(lcCommand)
ENDIF
ENDPROC
PROTECTED PROCEDURE UpdateSetting
EXECSCRIPT(This.SettingCommand) && Change the setting
ENDPROC
PROTECTED PROCEDURE RevertSetting
LOCAL lcCommand, lnStuffPosition, lnStuffLength, lcAliasWas
DO CASE
CASE This.SettingType = 0 && SET/ON
lnStuffPosition = AT(" ", this.Settingcommand, 2) + 1
lnStuffLength = LEN(this.settingcommand) - lnStuffPosition + 1
CASE This.SettingType = 1 && GOTO/GO
lnStuffPosition = AT(" RECORD ", UPPER(this.Settingcommand), 1) + 8
IF lnStuffPosition < 9
lnStuffPosition = AT(" ", this.Settingcommand, 1) + 1
lnStuffLength = AT(" ", this.Settingcommand, 2)
IF lnStuffLength > 0
lnStuffLength = lnStuffLength - lnStuffPosition + 1
ELSE
lnStuffLength = LEN(this.settingcommand) - lnStuffPosition + 1
ENDIF
ELSE
lnStuffLength = LEN(this.settingcommand) - lnStuffPosition + 1
ENDIF
CASE This.SettingType = 2 && SELECT
lnStuffPosition = AT(" ", this.Settingcommand, 1) + 1
lnStuffLength = LEN(this.settingcommand) - lnStuffPosition + 1
ENDCASE
IF !EMPTY(lnStuffPosition)
lcCommand = STUFF(This.SettingCommand, lnStuffPosition, lnStuffLength, TRANSFORM(this.PreviousValue))
EXECSCRIPT(lcCommand) && Put things back the way they were before class was instantiated
ENDIF
ENDPROC
ENDDEFINE