Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

ResetToDefault Bug 2

Status
Not open for further replies.

Olaf Doschke

Programmer
Oct 13, 2004
14,847
DE
Code:
o = CreateObject("myclass")
? o.myproperty && 0
o.ResetToDefault("myproperty")
? o.myproperty && .F.
Define Class myclass as Custom
   myproperty = 0
EndDefine

Expected Result: ResetToDefault should set a property to the value defined in the class instead of .F.

In VCX classes this works correctly, in classes defined in PRG code not.

This might really be a very old and still unnoted bug of very low importance, yet most probably the first discovered after support end three days ago at 13th of January.

Bye, Olaf.
 
Interesting and useful.
Thank you :)

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Yes, interesting.

It doesn't seem to depend on the data type of the property. You get same result if MyProperty is a string, date, or whatever. Even if you set MyProperty to .T. (in the class definition), it still displays as .F.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Useful? Maybe really, if you write code restting your objects you'll need to find a workaround for DEFINE CLASS or abondon them and work with VCX classes only.
Interestingly I know someone doing most things in PRG as all tables can get much easier corrupt than a PRG file, as it's simply text.

I'd say this is by design. Nothing to fear, as that must be a very old bug and nobody noticed means, nobody is really using it.

Maybe something along the lines of using the ADD OBJECT NOINIT clause in a PRG class generated at runtime to then get the default value of the sub object.

Something like this:

Code:
o = Createobject("myclass")
? o.myproperty && 0
o.ResetToDefault("myproperty")
? o.myproperty && .F.
? GetDefault(o,"myproperty")

Procedure GetDefault(oObj, cProp)
   Local lcScript
   TEXT To lcScript NOSHOW TEXTMERGE
      Clear Class defaultclass
      Local lo
      lo = CreateObject("defaultclass")
      Return GetPem(lo.o,"<<cProp>>")

      Define Class defaultclass as Custom
          Add Object o as <<oObj.class>> NOINIT
      EndDefine
   ENDTEXT

   Return Execscript(lcScript)
EndProc


Define Class myclass As Custom
   myproperty = 0

   Procedure Init()
      This.myproperty = 2 && change from default value 0 at init
   Endproc
Enddefine

It's quite essential you create the object with NOINIT, as you only want to know default values, you don't want to trigger all and any behaviour defined by init, especially not overriding the real default. At some situations you might actually want the value you have after the object establishes with finish of init(), then you have to go another route, eg simply create a further instance of the class with all the consequences of that.

There is an obvious catch here simply using oobj.class: It might be ambigous not sepcifying OF which prg or VCX. It's just a first draft of getting PRG class defaults.

Bye, Olaf.
 
Obvious error: The second line will now display 2 because of the class init I added to better demonstrate the difference NOINIT makes. Sorry for that.

Bye, Olaf.
 
> it still displays as .F.
At first I thought this is like the default .F. you get for any variable. It might come from that.

Bye, Olaf.
 
And of course:

Code:
Procedure PRGResetToDefault(oObj, cProp)
   Local lcScript
   TEXT To lcScript NOSHOW TEXTMERGE
      Clear Class defaultclass
      Local lo
      lo = CreateObject("defaultclass")
      Return GetPEM(lo.o,"<<cProp>>")

      Define Class defaultclass as Custom
          Add Object o as <<oObj.class>> NOINIT
      EndDefine
   ENDTEXT

   Store ExecScript(lcScript) To ("oObj."+cProp)
EndProc
 
Olaf said:
At first I thought this is like the default .F. you get for any variable. It might come from that.

Exactly my first impression.

The default value for any variable (except FOX and FOXPRO) when they are declared (but not assigned) is .f. so it's sort of what I'd actually expect in a VCX class as well if I just sit and think about it. It *IS* the default! The original assignment isn't the default.

The bug *could* be the behavior of VCX classes. [wink]
 
And finally as a method of the prg class itself:

Code:
o = Createobject("myclass")
? o.myproperty
o.MyResetToDefault("myproperty")
? o.myproperty

Define Class myclass As Custom
   myproperty = 0

   Procedure Init()
      This.myproperty = 1
   Endproc

   Procedure MyResetToDefault(cProp)
      If NOT SYS(1269,this,cProp,1) && property readonly
         Local lcScript
         TEXT To lcScript NOSHOW TEXTMERGE
            Clear Class defaultclass
            Local lo
            lo = CreateObject("defaultclass")
            Return GetPEM(lo.o,"<<cProp>>")

            Define Class defaultclass as Custom
               Add Object o as <<this.class>> NOINIT
            EndDefine
         ENDTEXT

         Store Execscript(lcScript) To ("This."+cProp)
      Endif
   Endproc
Enddefine

By the way I discovered the never before used SYS(1269) sys function to determine whether the property is readonly. You can guess how helpful the other parameterization is, for finding out whether a property changed from its default value. It doesn't indicate the default state correctly, eg it is .T. after o.ResetToDefault("myproperty"), while the value then is .F. and not the default 0.

Bye, Olaf.
 
>The bug *could* be the behavior of VCX classes

Well, but that would indeed be a useful VCX class bug. Otherwise how would you do anything alike to what the property window shows especially for your first subclasses of native base classes, a default value in the type you want a property to default to.

Judging from the help its meant the way it is for VCX classes:
ResetToDefault returns a property to the setting it had when first created. For example, if you have changed the font of a command button, calling this method for the caption resets the font to its default (Arial).

Notice: Arial, not .F.

Bye, Olaf.
 
Judging from the help its meant the way it is for VCX classes: ...

Notice: Arial, not .F.

Yes:

Code:
oForm = CREATEOBJECT("Form")
oForm.FontName = "Courier New"
oForm.ResetToDefault("FontName")
? oform.FontName     && Arial, not .F.

Mike




__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Useful? Maybe really, if you write code restting your objects you'll need to find a workaround for DEFINE CLASS or abondon them and work with VCX classes only.

I can't remember ever using ResetToDefault programmatically. Maybe I should. I wonder how common it is.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
More interesting is, if you subclass form in a vcx form and set the fontname different in the class designer of your form sub class, ResetToDefault also will just go back to that default value, not to .F. nor to Arial. Otherwise you'd need to subclass twice to let the last class level have the default value you set one level earlier.

Bye, Olaf.
 
Often enough the default is a very canonical value like empty string, 0 or today and so of less interest.

The discovery of the error was because someone wanted to know how to retrieve default values of classes for documenting purposes.

But I actually use ResetToDefault to reset to default values not defined in some constant but as the property default value. You don't need some constant or meta data for that, then. It's also nice to reset a color to the class definition, or even something like a tooltiptext that changes dynamically from a start text. You can use the default value as your template that way. It's as useful as SET SYSMENU TO DEFAULT or SET PRINTER TO DEFAULT. You don't have to know the default to get back to it and you can later change it in one place. In that aspect it's as useful as having a set of base classes is. Besides other things you actually use your base classes to set these defaults, eg the favored font or corporate identity color. You just normally don't change and want to get back to a default.

If you eg specify a function call as the default, eg set Caption to =SYS(2015), form.ResetToDefault("caption") then reexecutes SYS(2015), which might be what you don't want but also might be handy. You define an action and trigger it by ResetToDefault(). Of course you can also define a function and call it. There is nothing you can't do otherwise, but its nature of resetting things is useful.

Bye, Olaf.
 
Alternative solution is to store the default values and to intercept ResetToDefault()
Code:
CLEAR 
o = CreateObject("myclass")
? o.myproperty 
o.myproperty = 1
? o.myproperty 
o.ResetToDefault("myproperty")
? o.myproperty 

? o.myotherproperty 
o.myotherproperty = SYS(2015)
? o.myotherproperty 
o.ResetToDefault("myotherproperty")
? o.myotherproperty 

Define Class myclass as Custom
	myproperty = 0
	myotherproperty = SYS(2015)
	ADD OBJECT default_keeper as custom 
	PROCEDURE Init
		LOCAL ln,lni,la[1]
		ln = AMEMBERS(la,This,0,'U')
		FOR lni = 1 TO m.ln
			This.default_keeper.AddProperty(m.la[m.lni],EVALUATE("This." + m.la[m.lni]))
		NEXT
	ENDPROC
	PROCEDURE ResetToDefault
		LPARAMETERS cProperty
		NODEFAULT 
		STORE EVALUATE("This.default_keeper."+cProperty) TO ("This."+cProperty)
	ENDPROC
EndDefine

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Yes, that would really make a snapshot of initial sates, you wouldn't mimic the reevaluation of expressions as ResetToDefault does, only keep the original values. As said this can be what you want and need. It's less intrusive.

Both our solutions won't handle runtime added properties by some flavor of AddProperty, both the AddProperty method of many classes (you could have base code in there) and the AddProperty function. The latter is hard to handle, does it cause any event, if the AddProperty function changes an object. Can the object react to that at all?

Bye, Olaf.



 
I agree this is just for fun, but it's Friday :)
Anyway, you're right about the real world objects. They can be very complex.
Code:
CLEAR 
o = CreateObject("myclass")
? o.myproperty 
o.myproperty = 1
? o.myproperty 
o.ResetToDefault("myproperty")
? o.myproperty 

? o.myotherproperty 
o.myotherproperty = SYS(2015)
? o.myotherproperty 
o.ResetToDefault("myotherproperty")
? o.myotherproperty 

?o.Height
o.Height = 1
?o.Height
o.ResetToDefault("Height")
?o.Height

o.addproperty("mythirdproperty",SECONDS())
?o.mythirdproperty
INKEY(0.2)
o.mythirdproperty = SECONDS()
?o.mythirdproperty
o.ResetToDefault("mythirdproperty")
?o.mythirdproperty



Define Class myclass as Custom
	myproperty = 0
	myotherproperty = SYS(2015)
	ADD OBJECT default_keeper as custom 
	PROCEDURE Init
		LOCAL ln,lni,la[1]
		ln = AMEMBERS(la,This,0,'U')
		FOR lni = 1 TO m.ln
			This.default_keeper.AddProperty(m.la[m.lni],EVALUATE("This." + m.la[m.lni]))
		NEXT
	ENDPROC
	PROCEDURE ResetToDefault
		LPARAMETERS cProperty
		IF PEMSTATUS(This.default_keeper,cProperty,5) 
			NODEFAULT 
			STORE EVALUATE("This.default_keeper."+cProperty) TO ("This."+cProperty)
		ENDIF
	ENDPROC
	PROCEDURE AddProperty
		LPARAMETERS cPropertyName, eNewValue, nVisiblity, cDescription
		DO CASE
		CASE PCOUNT() = 4
			This.default_keeper.AddProperty(m.cPropertyName, m.eNewValue, m.nVisiblity, m.cDescription)
		CASE PCOUNT() = 3
			This.default_keeper.AddProperty(m.cPropertyName, m.eNewValue, m.nVisiblity)
		CASE PCOUNT() = 2
			This.default_keeper.AddProperty(m.cPropertyName, m.eNewValue)
		OTHERWISE
			This.default_keeper.AddProperty(m.cPropertyName)
		ENDCASE
	ENDPROC
EndDefine

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Fortunately we don't have to care for compound objects with sub objects on the root level, each object simply has its own ResetToDefault and also his own default_keeper. Maybe call that default_safe.

Bye, Olaf.
 
You could create a cursor from the property names AMEMBER returns, perhaps even via CREATE CURSOR FROM ARRAY after having modified the array to define table fields. Then GATHER FROM NAME THIS, then SCATTER NAME THIS.DEFAULT_SAFE. It won't really be more elegant, though, as you need to create and dispose a cursor just for transferring the values.

Bye, Olaf.
 
You're right :)
Thanks for this. It is fun. Reminds me the weekend tests posted by Mr. Mike Lewis recently.


Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top