Coding a Setup Frame
The way you define the contents and behavior of a setup frame depends entirely on the changes you need to make to the frame or frames to which the setup frame will be applied. Those changes will be applied to the edited frame and saved when that frame is saved.
In general, your 4GL code can be as simple or as sophisticated as the changes require, but you must ensure that it works and does not damage the edited frame. We recommend that you keep setup frames as simple as requirements permit, and to clearly describe their purpose and functionality in your frame's comment header to help future maintainers.
Note: Actian Support cannot provide support for application frames modified in this way unless you can provide a test case in which satisfies all of the following requirements:
• The damaged frame fails
• The same frame but without setup run succeeds
• The setup frame itself is provided and can be applied by us to produce the damaged frame
• The tests you used to validate the setup frame are provided
These restrictions correspond to the ones we impose for assistant-based frame templates and field templates.
The setup frame can be in any application that the edited frame's application can access (the application itself, or any application it includes). The setup frame can also call or access other frames, procedures, or user classes, provided they are in the same application; applications included by the setup frame's application are ignored.
The frame must specify the two parameters "frame" and "restyled" in its initialize block, as follows:
initialize (
frame = FrameSource default null;
restyled = integer not null default FALSE;
)=
{
/*initialize executing code goes here*/
}
These parameters will be passed values when the setup frame is called from the Property Inspector, as follows:
frame
Specifies the frame currently being edited
When the edited frame is subsequently saved, any change the setup frame has applied to it will be saved. For example, if the setup frame executes the following line of code:
frame.BgColor = CC_RED;
the frame in the Frame Editor will display a red background.
restyled
Specifies whether the changes applied by the setup frame have altered the edited frame's stylesheet.
You must set this to TRUE if the stylesheet has been affected, to ensure that it is refreshed in the edited frame.
The frame must return ER_OK if the processing has been successful, and ER_FAIL if it has not. If the return is not ER_OK, the frame you were editing will automatically undo all changes since it was last saved.
Setup Frame Uses and Examples
The Setup Frame approach has a wide range of uses, of which the two most directly beneficial are applying custom transformations to numbers of fields in the frame, and loading predefined behaviors into the frame. Some details on these and other uses follow:
Provide style effects that the stylesheet is unable to provide
The OpenROAD stylesheet provides rich support for different interface styles, but there is a limit to what static settings can do, and there are many field-specific attributes that the stylesheet generic dialogs cannot address. In web interfaces, the style completion role is often taken by quantities of scripting language triggered at runtime. In OpenROAD, such completion steps can be applied during development, leaving the runtime frame code clean and concise.
For example, if the stylesheet BgBitmap attribute is set for buttonfields, every individual buttonfield will have a copy of the same bitmapobject. It may be advantageous to use a single bitmapobject that all of them reference. The following code in a setup frame would achieve this:
initialize(
frame = FrameSource default null;
restyled = integer not null default FALSE;
)=
declare
fields = array of FormField default null;
I = integer not null;
enddeclare
{
fields = frame.TopForm.FieldsByProperty(
attributename='classname', searchstring='BUTTONFIELD');
for i = 1 to fields.LastRow do
if fields[i].BgBitmap is null then
continue;
elseif i = 1 then
sharedbitmap = fields[i].BgBitmap;
elseif sharedbitmap.LocateBitmap(
bitmap=fields[i].BgBitmap, match=TRUE) = ER_OK then
fields[i].BgBitmap = sharedbitmap;
endif;
endfor;
return ER_OK;
}
Apply "defined behaviors" that must be stored with the frame or its fields
This is a particularly important use, because these defined behaviors enable OpenROAD to simulate a wide range of styles and visual effects without the need for runtime code. For information about defined behaviors and how to specify them, see the InputEvent LoadEventBehavior method in the Language Reference Guide.
For example, the following code would ensure that each of the selected fields in your Frame Editor displayed a "highlight" background whenever the mouse was over them and reverted to their normal background when the mouse left the field:
declare
i = integer not null;
enterkey = varchar(32) not null;
exitkey = varchar(32) not null;
loresponse = StringObject;
hiresponse = StringObject;
fields = array of FormField default null;
IE = InputEvent;
enddeclare
…
/*
** Define the appropriate responses (in this case, providing the indexes of the
** 'hi' and 'lo' images in the multimage BgBitmap in each of these fields is
** sufficient to define the response). For information on OpenROAD multiimage
** bitmaps, see the BitmapObject ComposeBitmap Method in the Language Reference
** Guide.
*/
hiresponse.Value = '2';
loresponse.Value = '1';
/*
** Define the appropriate eventkeys,to ensure the highlighting is triggered when
** the mouse enters and exits the field
*/
enterkey = IE.EventKey(action=IE_MOUSEENTER,modifierkey=KB_NONE, responsecode=RE_USERDEFAULT);
exitkey = IE.EventKey(action=IE_MOUSEENTER, modifierkey=KB_NONE, responsecode=RE_USERDEFAULT);
/*
** Load the highlight behaviors into the selected fields
*/
fields = frame.SelectedList;
for i = 1 to fields.LastRow do
iE.LoadEventBehavior(location=fields[i], responsetype='event_responses',
eventkey=enterkey, response=hiresponse, locationinsource=TRUE);
iE.LoadEventBehavior(location=fields[i], responsetype='event_responses',
eventkey=exitkey, response=loresponse, locationinsource=TRUE);
endfor;
Apply custom transformations to any number of fields, with very precise targeting
Often such changes can be made using the Property Inspector but are laborious and error-prone, particularly for properties that can be set only one field at a time, or ones involving a combination of settings that must all be present and correct, or ones where the combination to apply varies depending on other settings.
For example, the following code designates that all pages in a tabfolder have the same properties as the selected one:
selpage = frame.SelectedList[1];
pages = TabFolder(selpage.ParentField).TabPageArray;
for i = 1 to pages.LastRow do
if pages[i] = selpage then
continue;
endif;
pages[i].SetAttribute(
bgcolor=selpage.BgColor,
bgpattern=selpage.BgPattern,
typeface=selpage.TypeFace,
…);
endfor;
Change properties that are not addressed by the Property Inspector or by any of the menu options in the frame editor toolbar
OpenROAD attempts to make all frame and field properties that should be set at design-time available through the Property Inspector or the Frame Editor toolbar, but this is not always possible or appropriate. In particular, some of the subfield settings of TabFolders, TableFields, TreeViewFields, and ListViewFields are too difficult or confusing to access or set safely through a visual editor, and must be applied programmatically.
Add and remove debugging-assist settings to the frame
Although the OpenROAD debugger provides powerful break and monitoring capabilities, you sometimes need to intervene directly in the code, for example, to measure performance of a particular statement, and adding and removing such statements puts the frame at risk.
The following code in a setup frame adds before and after statements to all instances in the edited frame of a particular method or procedure call—the before and after statements can invoke performance services or other trace facilities—and the subsequent code removes them.
Add:
/*
** Get the individual statements in the 4GL script
*/
statements = frame.Script.Split(delimiter=';\,{\,}',
includedelimiter=TRUE,
preserveleadingwhitespace=TRUE,
preserveblanklasttoken=TRUE);
/*
** Identify those with the specified call to be traced
*/
while startrow <= statements.LastRow do
rc = statements.Find(attributename='value', searchstring='%'+procname+'(%',
casesensitive=FALSE, rownumber=Byref(indx),
startrow=startrow, endrow=statements.LastRow);
if rc != ER_OK then
endloop;
endif;
instances[instances.LastRow+1].Value = indx;
startrow = indx+1;
endwhile;
/*
** Insert pre and post statements and reconstitute the script
*/
for i = instances.LastRow downto 1 do
indxtxt = Varchar(instances[i].Value);
predbg.Value = 'callproc myTracer(pre=TRUE, indx='+ indxtxt + '; ' + HC_NEWLINE;
postdbg.Value = 'callproc myTracer(pre=FALSE, indx='+ indxtxt + ';'+ HC_NEWLINE;
statements.InsertRow(rownumber=indx+2, rowobject=postdbg.Duplicate());
statements.InsertRow(rownumber=indx, rowobject=predbg.Duplicate());
endfor;
frame.Script.Join(strings=statements);
Remove:
/*
** Get the individual statements in the 4GL script
*/
statements = frame.Script.Split(delimiter=';\,{\,}',
includedelimiter=TRUE,
preserveleadingwhitespace=TRUE,
preserveblanklasttoken=TRUE);
/*
** Identify those with the specified call to be removed
*/
while startrow <= statements.LastRow do
rc = statements.Find(attributename='value', searchstring='%callproc myTracer(%',
casesensitive=FALSE, rownumber=Byref(indx),
startrow=startrow, endrow=statements.LastRow);
if rc != ER_OK then
endloop;
endif;
instances[instances.LastRow+1].Value = indx;
startrow = indx+1;
endwhile;
/*
** Remove the tracer statements and reconstitute the original script
*/
for i = instances.LastRow downto 1 do
statements.RemoveRow(rownumber=indx);
endfor;
frame.Script.Join(strings=statements);