OBJECT SET ENABLED returns 0, not true or false v18 R2

Today I moved this application from v18 to v18 R2 on MacOS Catalina, now it is beset with errors _O_ENABLE BUTTON(varname) which I can only get rid of by changing huge numbers of instances in the code to OBJECT SET ENABLED(true) or OBJECT SET ENABLED(false).

Errors did not happen in v18. Why this change? App has been running for 15 years, many 4D versions.

Furthermore, OBJECT SET ENABLED(true) is erratic, it works on some buttons - maybe - but not on others, whereas OBJECT SET ENABLED(false) does work all the time so this breaks the interface well and truly.

In the debugger the value returned by OBJECT SET ENABLED(true) is 0 rather than the True that I assume it should be (am I right?).

By the way all button names are unique variable names, object names are never used to set the button status.

I see that there are two bug fixes in R2 relating to buttons: previously buttons were not disabled correctly and also they were not greyed out. This was true of my application.

So now the buttons are grey when disabled but they are also disabled when they should be enabled.

Does this mean that there is a remaining bug / new bug which is preventing enabling?

The command does not have a return value, so on the debugger it should show undefined. Or, do you mean, after you disable an object, you check with OBJECT Get enabled? In any case, the command is not just a rebranding of _o_ENABLE BUTTON. Otherwise 4D would have internalised the conversion and made the change transparent.

The incompatibility urges us to reevaluated our code, which is important because the old command was based on an old coding paradigm, where we specify a form object by its variable name (data source), and all form objects that reference the same variable are “entangled” to each other and their data source. That’s now how we do business anymore. We want to use object names for states that are specific to the user interface.

At the very least, one should not be mixing the new and old commands in the same form, or with standard actions that alter the enabled state automatically, such as “next page”. It may work, but one should normally be consistent. In addition to that, the old command no longer works in v18 project mode.

1 Like

_O_ENABLE BUTTON and _O_DISABLE BUTTON have been removed in v18R2 (see here). I have the same issue, 1000+ calls to those commands that will need to be converted. Just the way it goes when you have a code base that has been around for a long time.

If you are testing that in v18 without converting your obsolete commands over to the new ones then you will see results that appear to be erratic.

4D (I’m guessing) has two separate ways of tracking the state of an object, the old way and the new. If one of those ways has the object set to disabled then it gets displayed on screen as disabled.

_O_DISABLE BUTTON(*;"MyButton")  //disabled on screen
OBJECT SET ENABLED(*;"MyButton";True)  //still disabled on screen
ASSERT(OBJECT Get enabled(*;"MyButton");"")  //assertion passes even though on screen it is still disabled

OBJECT SET ENABLED(*;"MyButton";False)  //still disabled on screen
_O_ENABLE BUTTON(*;"MyButton")  //still disabled on screen

OBJECT Set enabled(*;"MyButton";True)  //now it is enabled on screen because both ways have set the object as enabled

The “old” way (_o_enable/disable) acts on all instances of the variable, whatever the form. I often see that in old apps: form A opens form B, both have a button bCancel (variable). If code disables bCancel in form B, when closing form B, you’ll find the bCancel in form A disabled too.

The “new” way (object set/get enabled) acts only at the current form level, no such side effect.

Most of the time, I replace old by new without asking me questions. It may be a problem only if the developer using old way knew that, quite rare.

To narrow this down I have concentrated on just one button which has relative few instances in the code (around six places). I used Find in Design to search, this had found one extra instance that I had not discovered previously while dealing with the individual error messages from the user interface.

So now they all used either OBJECT SET ENABLED(BUmyButton;true) or OBJECT SET ENABLED(BUmyButton;false).

This had no effect, the button still remain disabled (and grey) on the form.

In the debugger the value of myButton is 0 in the expression pane while with mouse hover in the code line it is myButton: Long Integer=O

Next I changed all the instances so that they used the object name instead of the varname so they are now all OBJECT SET ENABLED(*;“obBUmyButton”;true/false).

The form button remained disabled and greyed out.

The only difference is that the debugger value is now undefined. I added a code line
vEnabled:=Object Get enabled and in the debugger this returns false immediately after it should have been enabled by the preceding line which is OBJECT SET ENABLED(*;“obBUmyButton”;true)

So I can’t conclude that this is anything other than a bug with enable.

What could I be missing here?

I am working on converting all instances of the old command to the new (some more recent code already used the new) but what is bothering me is that this does not seem to fix the enable issue.

I have tested using the example of just one button; see my reply to Miyako.

Are you finding that updating the command works correctly in all cases? And are you on v18 R2 ?

Did you delete the var name in properties?

Nooo … but why should I do that?
I have always given all buttons a var name plus an object name, although I don’t use the latter. So the database has both in form properties throughout.

Nevertheless I have tried your suggestion with the button I am focussing on right now, it does not change anything.

Regarding using the object name or the varname with Object Set Enable, according to the docs this is a matter of choice anyway; do you have a different take on this?

2 reasons…

1/ doing this, the button becomes autonomous, completely dissociated from the variable, exists only in the form. This way you’ll be sure that there is no side effect from the variable if it’s used elsewhere, not declared correctly, and so on.

2/ to reduce the number of process variables (has to be declared in COMPILER_variables, is forked each time you create a new process, takes up space in memory, etc.)
When “expression or variable name” is empty, a button goes like this:

$myButtonPointer:=OBJECT get pointer(object named;"myButtonName")  //pointer on the button
$myButtonValue:=$myButtonPointer->  //value of the button, 0 or 1
2 Likes

It’s worth doing this by code, very little risk of side effect…

SOLVED - with a deep, deep blush.

When I began changing all the button instances to the new form of the command OBJECT SET ENABLED the tedium and error prone nature of doing this manually in many places defeated my concentration and in some instances of particular buttons I inadvertently changed it to OBJECT SET ENTERABLE .

It was late at night after a long day …

I have fixed this on the one button I have been focussing on for testing the issue and it now enables correctly.

A big sorry to all who have read and replied on the thread.

Yes, I see that it is a good reason.

I have always taken great care to name button variables so that they are unique to their location but I accept that mistakes are possible as a database grows.

Witness my lapse of concentration in converting some instances of OBJECT SET ENABLED - see my solved post.

Thanks

Are there examples of code to do this across a database?

I’d seen that behaviour and just accepted that if the two forms are in the same process then that can happen. Never really gave it much thought and just dealt with it. Good to know that the new commands work at the form level though.

Definitely. I have this, plus _O_C_STRING and _O_ARRAY STRING that I need convert using code.

I can’t remember but here’s a short starting example:

  //$find_t:="_O_ENABLE BUTTON"
$find_t:="_O_DISABLE BUTTON"
METHOD GET PATHS(Path all objects;$path_at)
For ($i_l;1;Size of array($path_at))
	METHOD GET CODE($path_at{$i_l};$code_t)
	$isModified_b:=False
	$lineOfCode_c:=Split string($code_t;"\r")
	For each ($lineOfCode_t;$lineOfCode_c)
		Case of 
			: (Position($find_t;$lineOfCode_t)=1)
				  //replace old command with new one and set $isModified_b, 
				  //or open the method, etc.
		End case 
	End for each 
	If ($isModified_b)
		$code_t:=$lineOfCode_c.join("\r")
		METHOD SET CODE($path_at{$i_l};$code_t)
	End if 
End for 

That said, it will not help you if you want to replace variable names by object names at the same time.

2 Likes

Thank you, I will look into doing that. I have manually converted many now but there are still several hundred to go.

I found a piece of code with regex to help:

//2 patterns
ARRAY LONGINT($pos_al;0)
ARRAY LONGINT($len_al;0)
$rxEnable_t:="^_O_ENABLE BUTTON\\((.*)\\)"
$rxDisable_t:="^_O_DISABLE BUTTON\\((.*)\\)"
//then in the loop on lines of code above
Case of 
	: (Match regex($rxEnable_t;$lineOfCode_t;1;$pos_al;$len_al))
		$varName_t:=Substring($lineOfCode_t;$pos_al{1};$len_al{1})
		$endOfLine_t:=Substring($lineOfCode_t;$pos_al{0}+$len_al{0}+1)
		$newLine_t:="OBJECT SET ENABLED("+$varName_t +";True)"+$endOfLine_t
	: (Match regex($rxDisable_t;$lineOfCode_t;1;$pos_al;$len_al))
		$varName_t:=Substring($lineOfCode_t;$pos_al{1};$len_al{1})
		$endOfLine_t:=Substring($lineOfCode_t;$pos_al{0}+$len_al{0}+1)
		$newLine_t:="OBJECT SET ENABLED("+$varName_t +";False)"+$endOfLine_t
End case 

2 Likes

I am attaching two methods that will update all of these obsolete commands and more. It may be that you have to run when commands are still commands

(Attachment ReplaceInCompiler_methods.txt is missing)

(Attachment Replace_CompilerDirectives.txt is missing)

OK here is the code

  // Method Replace_compiler_directives replaces deprecated compiler
  //directives
  // C_INTEGER, _o_C_INTEGER
  // C_STRING, _o_C_STRING
  // ARRAY STRING, _o_ARRAY STRING
  //
  // #SYNTAX: Replace compiler directives
  // #PARAMETERS:
  // none

  // #DATE CREATION: 02/02/2016 #AUTHOR: Bertrand SOUBEYRAND
  //info@soubeyrand-4d-developer.eu
  // #DATE MODIFICATION: 08/02/2016
  // #NOTE: This methods runs on 4D v13 and later
  //
  // #HEADER VERSION: 2

  // If you find this code useful, so send a mail!

C_LONGINT($L_Milli;$L_Pos)
$L_Milli:=Milliseconds

ARRAY TEXT($rT_Paths;0)
ARRAY TEXT($rT_Code;0)
METHOD GET PATHS(Path all objects;$rT_Paths)

METHOD GET CODE($rT_Paths;$rT_Code)
C_TEXT($T_This_path)

$T_This_path:=METHOD Get path(Path project method;Current method name)
$L_Pos:=Find in array($rT_Paths;$T_This_path)
DELETE FROM ARRAY($rT_Paths;$L_Pos)
DELETE FROM ARRAY($rT_Code;$L_Pos)
$T_This_path:=METHOD Get path(Path project method;"Replace_compilerdirectives")

$L_Pos:=Find in array($rT_Paths;$T_This_path)
DELETE FROM ARRAY($rT_Paths;$L_Pos)
DELETE FROM ARRAY($rT_Code;$L_Pos)

C_TEXT($NewLine_txt)
ARRAY TEXT($rT_Old_Directive;6)
ARRAY TEXT($rT_New_Directive;Size of array($rT_Old_Directive))

  // added 02/08/2016
$rT_Old_Directive{1}:=Command name(293)+"\\(\\d{1,3};"  // c_string
$rT_Old_Directive{2}:=Command name(282)+"\\("  // c_integer
$rT_Old_Directive{3}:=Command name(218)+"\\(\\d{1,3};"  // array string
$rT_Old_Directive{4}:="\r(_O_ENABLE BUTTON)(\\()(.)(\\))(.)\r"  // _o_ENABLE BUTTON
$rT_Old_Directive{5}:="\r(_O_DISABLE BUTTON)(\\()(.)(\\))(.)\r"  // _o_DISABLE BUTTON
$rT_Old_Directive{6}:="\r(_O_REDRAW LIST)(\\()(.)(\\))(.)\r"  // _o_DISABLE BUTTON

$rT_New_Directive{1}:=Command name(284)+"("
$rT_New_Directive{2}:=Command name(283)+"("
$rT_New_Directive{3}:=Command name(222)+"("
$rT_New_Directive{4}:=Command name(1123)+"("
$rT_New_Directive{5}:=Command name(1123)+"("
$rT_New_Directive{6}:="REDRAW("

C_LONGINT($i)
For ($i;1;Size of array($rT_Paths))
	
	  // for each method
	
	C_BOOLEAN($B_Modified)
	$B_Modified:=False
	
	C_LONGINT($j)
	
	  // for each directive
	
	C_LONGINT($L_Start)
	$L_Start:=1
	
	  // for each found
	
	ARRAY LONGINT($rL_Pos;0)
	ARRAY LONGINT($rL_Length;0)
	
	C_BOOLEAN($B_Match)
	$L_Start:=Position("O_C_string(";$rT_Code{$i};1)
	C_LONGINT($InnerLoop_l)
	
	If ($L_Start>0)
		ARRAY TEXT($Lines_atxt;0)
		$B_Modified:=True
		
		ut_TextToArray($rT_Code{$i};->$Lines_atxt;Char(Carriage return))
		For ($InnerLoop_l;1;Size of array($Lines_atxt))
			$L_Start:=Position("O_C_string(";$Lines_atxt{$InnerLoop_l})
			If ($L_Start>0)
				ARRAY TEXT($Parts_atxt;0)
				ut_TextToArray($Lines_atxt{$InnerLoop_l};->$Parts_atxt;";")
				If (Size of array($Parts_atxt)=3)
					$NewLine_txt:=Replace string($Parts_atxt{1};"_O_C_string(";"C_Text(")
					$NewLine_txt:=$NewLine_txt+";"+$Parts_atxt{3}+")// was length of "+$Parts_atxt{2}
					$Lines_atxt{$InnerLoop_l}:=$NewLine_txt
					
				End if 
				
			End if 
		End for 
	End if 
	
	If ($B_Modified)
		ut_SaveDirectiveChanges($rT_Paths{$i};"Before")
		$NewLine_txt:=""
		For ($InnerLoop_l;1;Size of array($Lines_atxt))
			$NewLine_txt:=$NewLine_txt+$Lines_atxt{$InnerLoop_l}+Char(Carriage return)
			
		End for 
		METHOD SET CODE($rT_Paths{$i};$NewLine_txt)
		ut_SaveDirectiveChanges($rT_Paths{$i};"After")
	End if 
End for 

ALERT("job done in "+String((Milliseconds-$L_Milli)*0.001)+" seconds")

  // EOM

  // Method Replace_compiler_directives replaces deprecated compiler
  //directives
  // C_INTEGER, _O_C_INTEGER
  // C_STRING, _O_C_STRING
  // ARRAY STRING, _o_ARRAY STRING
  //
  // #SYNTAX: Replace compiler directives
  // #PARAMETERS:
  // none

  // #DATE CREATION: 02/02/2016 #AUTHOR: Bertrand SOUBEYRAND
  //info@soubeyrand-4d-developer.eu
  // #DATE MODIFICATION: 08/02/2016
  // #NOTE: This methods runs on 4D v13 and later
  //
  // #HEADER VERSION: 2

  // If you find this code useful, so send a mail!

C_LONGINT($L_Milli;$L_Pos;$ParenPos_L)
$L_Milli:=Milliseconds
C_TEXT($cmts_txt)
ARRAY TEXT($rT_Paths;0)
ARRAY TEXT($rT_Code;0)
METHOD GET PATHS(Path all objects;$rT_Paths)

METHOD GET CODE($rT_Paths;$rT_Code)
C_TEXT($T_This_path)
$T_This_path:=METHOD Get path(Path project method;Current method name)
$L_Pos:=Find in array($rT_Paths;$T_This_path)
DELETE FROM ARRAY($rT_Paths;$L_Pos)
DELETE FROM ARRAY($rT_Code;$L_Pos)
$T_This_path:=METHOD Get path(Path project method;"ReplaceInCompiler_methods")

$L_Pos:=Find in array($rT_Paths;$T_This_path)
DELETE FROM ARRAY($rT_Paths;$L_Pos)
DELETE FROM ARRAY($rT_Code;$L_Pos)

ARRAY TEXT($rT_Old_Directive;6)
ARRAY TEXT($rT_New_Directive;Size of array($rT_Old_Directive))

  // added 02/08/2016
$rT_Old_Directive{1}:=Command name(293)+"\\(\\d{1,3};"  // c_string
$rT_Old_Directive{2}:=Command name(282)+"\\("  // c_integer
$rT_Old_Directive{3}:=Command name(218)+"\\(\\d{1,3};"  // array string
$rT_Old_Directive{4}:="\r(_O_ENABLE BUTTON)(\\()(.)(\\))(.)\r"  // _o_ENABLE BUTTON
$rT_Old_Directive{5}:="\r(_O_DISABLE BUTTON)(\\()(.)(\\))(.)\r"  // _o_DISABLE BUTTON
$rT_Old_Directive{6}:="\r(_O_REDRAW LIST)(\\()(.)(\\))(.)\r"  // _o_DISABLE BUTTON

$rT_New_Directive{1}:=Command name(284)+"("
$rT_New_Directive{2}:=Command name(283)+"("
$rT_New_Directive{3}:=Command name(222)+"("
$rT_New_Directive{4}:=Command name(1123)+"("
$rT_New_Directive{5}:=Command name(1123)+"("
$rT_New_Directive{6}:="REDRAW("

C_LONGINT($i)
For ($i;1;Size of array($rT_Paths))
	
	  // for each method
	
	C_BOOLEAN($B_Modified)
	$B_Modified:=False
	
	C_LONGINT($j)
	For ($j;1;Size of array($rT_Old_Directive))
		
		  // for each directive
		
		C_LONGINT($L_Start)
		$L_Start:=1
		Repeat 
			  // for each found
			
			ARRAY LONGINT($rL_Pos;0)
			ARRAY LONGINT($rL_Length;0)
			
			C_BOOLEAN($B_Match)
			$B_Match:=Match regex($rT_Old_Directive{$j};$rT_Code{$i};$L_Start;$rL_Pos;$rL_Length)
			If ($B_Match)
				
				$B_Modified:=True
				
				$L_Start:=$rL_Pos{0}+$rL_Length{0}
				
				C_TEXT($T_Old)
				$T_Old:=Substring($rT_Code{$i};$rL_Pos{0};$rL_Length{0})
				
				If (Position("O_C_string";$T_Old)>0)
					$rT_Code{$i}:=Replace string($rT_Code{$i};$T_Old;$rT_New_Directive{$j})
					$ParenPos_L:=Position(")";$rT_Code{$i};$rL_Pos{0})
					$T_Old:=Replace string($T_Old;"_O_C_STRING(";"// string length was ";1)""
					$T_Old:=Replace string($T_Old;";";"";1)""
					
					$rT_Code{$i}:=Substring($rT_Code{$i};1;$ParenPos_L)+$T_Old+Substring($rT_Code{$i};$ParenPos_L+1)
					
				End if 
				
				If (Position("_O_ARRAY STRING";$T_Old)>0)
					$rT_Code{$i}:=Replace string($rT_Code{$i};$T_Old;$rT_New_Directive{$j})
					$ParenPos_L:=Position(")";$rT_Code{$i};$rL_Pos{0})
					$T_Old:=Replace string($T_Old;"_o_ARRAY STRING(";"//array string length was ";1)
					$T_Old:=Replace string($T_Old;";";"";1)""
					If (Substring($rT_Code{$i};$ParenPos_L;2)="))")
						$rT_Code{$i}:=Substring($rT_Code{$i};1;$ParenPos_L+2)+$T_Old+Substring($rT_Code{$i};$ParenPos_L+2)
					Else 
						$rT_Code{$i}:=Substring($rT_Code{$i};1;$ParenPos_L)+$T_Old+Substring($rT_Code{$i};$ParenPos_L+1)
					End if 
				End if 
				
				If (Position("_O_C_INTEGER";$T_Old)>0)
					$rT_Code{$i}:=Replace string($rT_Code{$i};$T_Old;$rT_New_Directive{$j})
					$ParenPos_L:=Position(")";$rT_Code{$i};$rL_Pos{0})
					$T_Old:=Replace string($T_Old;"_O_C_INTEGER(";"// was integer";1)
					$T_Old:=Replace string($T_Old;";";"";1)
					
					$rT_Code{$i}:=Substring($rT_Code{$i};1;$ParenPos_L)+$T_Old+Substring($rT_Code{$i};$ParenPos_L+1)
				End if 
				
				If (Position("_O_ENABLE BUTTON";$T_Old)>0)
					
					$cmts_txt:=""
					If (Size of array($rL_Pos)=5)
						$cmts_txt:=Substring($rT_Code{$i};$rL_Pos{5};$rL_Length{5})
					End if 
					$rT_Code{$i}:=Substring($rT_Code{$i};1;($rL_Pos{0}))+" Command Replaced "+Substring($rT_Code{$i};$rL_Pos{0}+1;($rL_Length{0}-1))+"OBJECT SET ENABLED("+Substring($rT_Code{$i};$rL_Pos{3};$rL_Length{3})+"; true)"+$cmts_txt+"\r"+Substring($rT_Code{$i};($rL_Pos{0}+$rL_Length{0}))  //$start_L:=$start_aL{0}+$rL_Length{0}+Length(" Command Replaced ")
					  //$T_Old:=Replace string($T_Old;"_o_ENABLE BUTTON(";"// was _o_ENABLE BUTTON ";1)
					  //$T_Old:=Replace string($T_Old;";";"";1)
					
					  //$rT_Code{$i}:=Substring($rT_Code{$i};1;$ParenPos_L)+$T_Old+Substring($rT_Code{$i};$ParenPos_L+1)
					
				End if 
				If (Position("_O_DISABLE BUTTON";$T_Old)>0)
					
					$cmts_txt:=""
					If (Size of array($rL_Pos)=5)
						$cmts_txt:=Substring($rT_Code{$i};$rL_Pos{5};$rL_Length{5})
					End if 
					
					$rT_Code{$i}:=Substring($rT_Code{$i};1;($rL_Pos{0}))+" Command Replaced "+Substring($rT_Code{$i};$rL_Pos{0}+1;($rL_Length{0}-1))+"OBJECT SET ENABLED("+Substring($rT_Code{$i};$rL_Pos{3};$rL_Length{3})+"; false)"+$cmts_txt+"\r"+Substring($rT_Code{$i};($rL_Pos{0}+$rL_Length{0}))  //$start_L:=$start_aL{0}+$rL_Length{0}+Length(" Command Replaced ")
					  //$T_Old:=Replace string($T_Old;"_o_ENABLE BUTTON(";"// was _o_ENABLE BUTTON ";1)
					  //$T_Old:=Replace string($T_Old;";";"";1)
					
					  //$rT_Code{$i}:=Substring($rT_Code{$i};1;$ParenPos_L)+$T_Old+Substring($rT_Code{$i};$ParenPos_L+1)
					
				End if 
				
				If (Position("_O_REDRAW";$T_Old)>0)
					
					$cmts_txt:=""
					If (Size of array($rL_Pos)=5)
						$cmts_txt:=Substring($rT_Code{$i};$rL_Pos{5};$rL_Length{5})
					End if 
					
					$rT_Code{$i}:=Substring($rT_Code{$i};1;($rL_Pos{0}))+" Command Replaced "+Substring($rT_Code{$i};$rL_Pos{0}+1;($rL_Length{0}-1))+"redraw("+Substring($rT_Code{$i};$rL_Pos{3};$rL_Length{3})+")"+$cmts_txt+"\r"+Substring($rT_Code{$i};($rL_Pos{0}+$rL_Length{0}))  //$start_L:=$start_aL{0}+$rL_Length{0}+Length(" Command Replaced ")
					  //$T_Old:=Replace string($T_Old;"_o_ENABLE BUTTON(";"// was _o_ENABLE BUTTON ";1)
					  //$T_Old:=Replace string($T_Old;";";"";1)
					
					  //$rT_Code{$i}:=Substring($rT_Code{$i};1;$ParenPos_L)+$T_Old+Substring($rT_Code{$i};$ParenPos_L+1)
					
				End if 
				
			End if 
			
		Until ($B_Match=False)
	End for 
	
	If ($B_Modified)
		ut_SaveDirectiveChanges($rT_Paths{$i};"Before")
		METHOD SET CODE($rT_Paths{$i};$rT_Code{$i})
		ut_SaveDirectiveChanges($rT_Paths{$i};"After")
	End if 
End for 

ALERT("job done in "+String((Milliseconds-$L_Milli)*0.001)+" seconds")

  // EOM

origanl was from Bertrand Soubeyrand

3 Likes

Thanks Charles.

You might want to repost that code though using the </> button. Other wise the forum will treat some of your code as formatting, and it won’t come out right.

E.g. I think the regex should be \\r(_O_ENABLE BUTTON)(\\()(.*)(\\))(.*)\\r, but it’s displaying as \r(_O_ENABLE BUTTON)(\()(.)(\))(.)\r