Everybody enjoys writing new code, but there is still legacy code out there to maintain. (And as I keep reminding
myself, the pay is the same whether I write fixed-format RPG or free-format RPG.) This service program and RPG/DDS test source was the result of a situation where a legacy application happened to be out of indicators, raising the issue of how to control display attributes without the use of an indicator.
I knew that I could control the display attributes of a field through the use of the DDS keyword DSPATR and a P-type (program-to-system) variable. To make the process simple and re-useable, I decided to create a prototyped procedure that makes it simple to manipulate the value of the display attribute in the P-type field.
The following source code describes a very simple service program, a display file and a bound RPG program. The objects may serve as an example for application development on a couple of levels. It demonstrates how an RPG program can be made to manipulate display attributes without using indicators. It also provides a step-by-step description of how to create a service program, and how to use a service program procedure via a bound RPGLE program.
The RPG source (Fig. 1) of the service program used to set field display color and other field attributes is represented below. The program's procedure accepts up to three parameters as input and returns the hex character that is necessary to define the display field's attribute.
The first parameter is designed to reflect the color attribute. The second parameter may be used to define an attribute other than color, such as UL (underline) or RI (reverse image). The third parameter is used to determine if the field is to be protected.
The D specs (in Fig.1) define the hex patterns that represent color and other attributes. The source code performs some simple tests up front to determine how many parameters have been passed. Since *OMIT is used in conjunction with *NOPASS, %ADDR is used instead of %PARM to see if the first parameter was passed, because *OMIT will be counted as a parameter even if it contains no value (null).
The body of the program logic sets the attributes and uses the %BITOR BIF to manipulate the bits representing combinations of color, underline, reverse image, and the protect attributes. The hex value passed back to the requesting application will cause fields referencing the P-type variable to show the resulting display attribute, defined by the bit-pattern.
To demonstrate the use of this application, first create a module from the source:
Then create a service program from the module:
CRTSRVPGM SRVPGM(MYLIB/UTDATRRI) EXPORT(*ALL) TEXT('Display attribute service program') ACTGRP(QILE)
In most cases, a service program will have multiple procedures defined. It is best to create a service source member to contain the procedures to be exported rather than specifying EXPORT(*ALL) on the command. But for the purpose of this example, and since this small service program contains only one procedure, EXPORT(*ALL) will suffice when you create the object.
Next create a binding directory:
CRTBNDDIR BNDDIR(MYLIB/UTDATRRI) TEXT('Binding directory for display attributes')
Add a binding directory entry: ADDBNDDIRE BNDDIR(MYLIB/UTDATRRI) OBJ((UTDATRRI *SRVPGM))
The binding directory serves as documentation and will allow you to use the 'H' spec within your RPGLE program, simplifying the program creation.
To test the service program, I created a simple display file and a program. The source above (Fig. 2) is a small pop-up window display file, UTDATRDP. This contains a record format to allow you to see the results of the display attribute values applied as calls to the service program change the program-to-system variable.
The display attribute of the field, Z$TXT1 is contained in the P-type field DSPATR. The field value manipulated in the RPG program will contain the hex representation of the display attribute, which defines how the device file is to present the field, in normal, high-intensity or underlined and determines whether the field is protected.
CRTDSPF FILE(MYLIB/UTDATRDP) RSTDSP(*YES)
The sample code below (Fig. 3) is a small RPG program using the display and bound to the service program UTDATRRI. Compile the RPG program UTDATRRP using the create bound RPG program command.
Since the 'H' spec contains the binding directory keyword, option 14 from your PDM member list (CRTBNDRPG) is all you need to create the program -- if you've created the binding directory mentioned in the instructions above. The test program is very simplistic, but does illustrate the basic relationship between a bound process and a service program, prototyping the named procedure from the service program it will use.
Call the program and press the enter key to see the results of the SetColor() procedure as it is invoked by the test program. The procedure with the variable parameter list makes manipulating the display attribute fairly simple. And you won't have to remember the hex values for the various attributes you need to apply to a field.
With the service program in place, all I have to do is SetColor() to define display attributes. That's all there is to it. Have fun with it!
There are other hex values you might want to include in your service program, such as column separators. If you chose to make a production application from this source, you might want to put some more sophisticated validity checking in the service program, such as a monitor block to manage exceptions, and perhaps you might add an error return code to the parameter list.
I wouldn't recommend this technique to control individual error conditions for a large number of screen fields on the same record format. It could get confusing naming a large number of P-type fields and setting individual values for a large number of fields. The technique could be effective, however, where a screen has all the fields protected (view only) in one mode and unprotected in an update mode. With the display attributes pointed to a common P-type field, all the screen fields could be input-capable, or protected with just a single line of RPG code (SetColor()) and no indicators.
In addition to the display attributes, the program also makes an interesting use of procedure prototypes, demonstrating two very useful options, *OMIT and *NOPASS. It is not usual to see the two used in conjunction. Just be careful of how you test the parameters when using *OMIT, and remember to use %ADDR instead of %PARMS to test whether a value has been passed or not.