As IBM continues to enhance RPG to allow
reducing the use of indicators, RPG developers should also be looking for ways
to decrease the use of indicators. Using display attribute fields is one way.
One common use of indicators is to conditionally set the display
attributes of fields on display files--for example, highlighting or reverse
imaging the field, or setting the field's color.
Setting the display
attributes on a field is done using the COLOR and DSPATR field-level keywords.
Different display attributes are usually set with conditioning indicators.
However, there is a way to set these attributes without using indicators--by
coding a special form of DSPATR and defining attribute fields.
An
attribute field is a one-character field defined in the display file DDS. It is
a program-to-system field (specified with a usage of "P"). The RPG application
loads an attribute byte into this field, which controls the display
attributes of the display field linked to the attribute field.
A display
field is linked to an attribute field by using the DSPATR keyword: The format is
DSPATR(&ATTRFLD), where ATTRFLD is the name of the attribute field. A field
can have only one attribute field, although any number of fields can share a
single attribute field; thus, one attribute field can be used to set the same
attributes for more than one field simultaneously.
Attribute Bytes
An attribute byte is a special one-byte value that
controls the color and effect of a display field. By "effect," I mean
highlighting, underlining, and reverse imaging. These values are expressed in
hexadecimal format--for example, x'20' (for normal attribute).
IBM's
documentation for the DSPATR keyword gives a list of 30 attribute byte values:
Some establish a color (such as x'38' for pink), some establish an effect (x'24'
for underscore), and some establish a combination (x'3C' for pink and
underscore). These 30 attribute byte values are used for input-capable fields.
There is another set of 30 values for use with non-input-capable fields. That is
a total of 60 values.
To set display attributes for a field, we simply
load the desired attribute byte value into the field's linked attribute
field:
ATTRFLD = x'24';
And then we write the screen format.
Attribute Value Bit Manipulation
It's easier, and recommended, to define a named
constant for each attribute byte value. It is also a good idea to put these
named constants into a copybook. We can define a named constant for each of the
60 values. However, we don't necessarily need to define that many named
constants.
These attribute values were not arbitrarily chosen. Each bit
of an attribute value has a certain function:
- The high order bit (bit 0 in
RPG-ese) is the Protected Attribute bit. It is on for a protected field, off for
an input-capable field.
- Bits 1 and 2 are always 0 and 1, respectively.
- Bits 3 and 4 determine the color (00=green, 01=red, 10=turquoise,
11=pink).
- Bit 5, if on, underscores the field.
- Bit 6, if on, highlights the field.
- Bit 7, if on, reverse images the field. Setting on bits 5, 6, and 7 at the
same time results in the non-display attribute.
We can then use
bit manipulation to derive any of the 60 attributes we want. Now, we would only
need a minimum of eight named constants:
- One constant for each of the four color attributes
- One constant for each of the three effect attributes
- One constant for the protect attribute
We derive the
attribute value by assigning the named constant to the attribute field:
ATTRFLD = atrPNK;
To combine color with an effect and/or the protect attribute, use the
%bitor built-in function (BIF). As an
example, to set an attribute field to pink with reverse image, you would use the
statement:
ATTRFLD = %bitor(atrPNK:atrRI);
Or, prior to V5R2:
C eval ATTRFLD = x'00'
C biton atrPNK ATTRFLD
C biton atrRI ATTRFLD
To determine if a certain attribute is on in an attribute field, use the
%bitand BIF. For any named constant atrXX, the format would be this:
if %bitand(ATTRFLD:atrXX) = atrXX;
For example, to check to see if the reverse image attribute is
on:
if %bitand(ATTRFLD:atrRI) = atrRI;
or
C testb atrRI ATTRFLD 99
C*** 99 is in the EQ indicator spot, positions 75..76
C if *in99
To check for a combination, use %bitor wherever atrXX is (or use an and
clause).
I mentioned that we would need only eight named constants, but
for the sake of a program's readability and ease of maintenance, I suggest a
couple more:
- Named constants for white, yellow and blue. These are
actually highlighting colors, derived by turning on highlighting with the
following colors: green, which highlights to white; turquoise, which highlights
to yellow; and pink, which highlights to blue.
- A named constant for the non-display attribute.
Example: Calendar Date Prompt Window
To best see attribute fields in action, I present
this example (which you may download and use).
This program is a date
prompt window. When called, it presents a calendar, from which the user may
select a date to pass back to the caller, either by double-clicking on the
desired day, or by putting the cursor on the desired day and pressing
Enter.
As you can see in the figure above, the
days in the calendar have various attributes. Workdays are green. Weekends and
holidays are pink. Today's date is reversed imaged.
The program has three
parameters:
- A one-digit numeric field that returns either zero (if the user
exits the prompt without selecting a day) or a number representing the day of
the week of the user's selected day (1=Monday...7=Sunday).
- An *ISO date field that contains the user's selected date. (Careful! If the
user exits without selecting a date, this will contain the system date.)
- An optional one-character field (defined in the program as an indicator
field) that contains '1' if the selected date is a workday, '0'
otherwise.
Display File Source (PMTCALD)
In order for this program to work properly and for
the calendar to be displayed properly, we need to set up 42 fields--one for each
day of the month, keeping in mind that a month could span six calendar week rows
(as May 2004 does). Also, keep in mind that the first of the month could start
on any of the seven days of the week.
The goal is to differentiate
between workdays (which may include weekend days) and non-workdays, including
holidays. Non-workdays will be pink. To accomplish changing the attribute to
pink using indicators, we would need to use 42 separate indicators, one for each
day field. To also reverse image today's day, we would need another 42
separate indicators.
By using attribute fields, we will use no
indicators:
A*%%TS SD 20030923 143255 DECKERSLEY REL-V5R2M0 5722-WDS
A*%%EC
A DSPSIZ(24 80 *DS3)
A R SCRN1
A*%%TS SD 20030923 143255 DECKERSLEY REL-V5R2M0 5722-WDS
A WINDOW(*DFT 12 40)
A WDWBORDER((*COLOR WHT)-
(*DSPATR RI)-
A (*CHAR ' '))
A RTNCSRLOC(&CSRREC &CSRFLD)
A MOUBTN(*ULD E03)
A CSRREC 10A H
A CSRFLD 10A H
A CSRROW 3S 0H
A CSRCOL 3S 0H
1 A A01 1A P
A A02 1A P
A A03 1A P
A A04 1A P
A A05 1A P
A A06 1A P
A A07 1A P
A A08 1A P
A A09 1A P
A A10 1A P
A A11 1A P
A A12 1A P
A A13 1A P
A A14 1A P
A A15 1A P
A A16 1A P
A A17 1A P
A A18 1A P
A A19 1A P
A A20 1A P
A A21 1A P
A A22 1A P
A A23 1A P
A A24 1A P
A A25 1A P
A A26 1A P
A A27 1A P
A A28 1A P
A A29 1A P
A A30 1A P
A A31 1A P
A A32 1A P
A A33 1A P
A A34 1A P
A A35 1A P
A A36 1A P
A A37 1A P
A A38 1A P
A A39 1A P
A A40 1A P
A A41 1A P
A A42 1A P
A S1MONTH 2Y 0B 1 2EDTCDE(Z)
A PBMONTH 2Y 0B 1 6PSHBTNFLD((*GUTTER 2))
A PSHBTNCHC(1 '-' CF07)
A PSHBTNCHC(2 '+' CF08)
A S1MONTHN 9A O 1 15
A S1YEAR 4Y 0B 1 25EDTCDE(Z)
A PBYEAR 2Y 0B 1 31PSHBTNFLD((*GUTTER 2))
A PSHBTNCHC(1 '-' CF19)
A PSHBTNCHC(2 '+' CF20)
A 3 2'Su'
A DSPATR(UL)
A DSPATR(HI)
A 3 8'Mo'
A DSPATR(UL)
A DSPATR(HI)
A 3 14'Tu'
A DSPATR(UL)
A DSPATR(HI)
A 3 20'We'
A DSPATR(UL)
A DSPATR(HI)
A 3 26'Th'
A DSPATR(UL)
A DSPATR(HI)
A 3 32'Fr'
A DSPATR(UL)
A DSPATR(HI)
A 3 38'Sa'
A DSPATR(UL)
A DSPATR(HI)
2 A D01 2Y 0O 4 2EDTCDE(Z)
A DSPATR(&A01)
A D02 2Y 0O 4 8EDTCDE(Z)
A DSPATR(&A02)
A D03 2Y 0O 4 14EDTCDE(Z)
A DSPATR(&A03)
A D04 2Y 0O 4 20EDTCDE(Z)
A DSPATR(&A04)
A D05 2Y 0O 4 26EDTCDE(Z)
A DSPATR(&A05)
A D06 2Y 0O 4 32EDTCDE(Z)
A DSPATR(&A06)
A D07 2Y 0O 4 38EDTCDE(Z)
A DSPATR(&A07)
A D08 2Y 0O 5 2EDTCDE(Z)
A DSPATR(&A08)
A D09 2Y 0O 5 8EDTCDE(Z)
A DSPATR(&A09)
A D10 2Y 0O 5 14EDTCDE(Z)
A DSPATR(&A10)
A D11 2Y 0O 5 20EDTCDE(Z)
A DSPATR(&A11)
A D12 2Y 0O 5 26EDTCDE(Z)
A DSPATR(&A12)
A D13 2Y 0O 5 32EDTCDE(Z)
A DSPATR(&A13)
A D14 2Y 0O 5 38EDTCDE(Z)
A DSPATR(&A14)
A D15 2Y 0O 6 2EDTCDE(Z)
A DSPATR(&A15)
A D16 2Y 0O 6 8EDTCDE(Z)
A DSPATR(&A16)
A D17 2Y 0O 6 14EDTCDE(Z)
A DSPATR(&A17)
A D18 2Y 0O 6 20EDTCDE(Z)
A DSPATR(&A18)
A D19 2Y 0O 6 26EDTCDE(Z)
A DSPATR(&A19)
A D20 2Y 0O 6 32EDTCDE(Z)
A DSPATR(&A20)
A D21 2Y 0O 6 38EDTCDE(Z)
A DSPATR(&A21)
A D22 2Y 0O 7 2EDTCDE(Z)
A DSPATR(&A22)
A D23 2Y 0O 7 8EDTCDE(Z)
A DSPATR(&A23)
A D24 2Y 0O 7 14EDTCDE(Z)
A DSPATR(&A24)
A D25 2Y 0O 7 20EDTCDE(Z)
A DSPATR(&A25)
A D26 2Y 0O 7 26EDTCDE(Z)
A DSPATR(&A26)
A D27 2Y 0O 7 32EDTCDE(Z)
A DSPATR(&A27)
A D28 2Y 0O 7 38EDTCDE(Z)
A DSPATR(&A28)
A D29 2Y 0O 8 2EDTCDE(Z)
A DSPATR(&A29)
A D30 2Y 0O 8 8EDTCDE(Z)
A DSPATR(&A30)
A D31 2Y 0O 8 14EDTCDE(Z)
A DSPATR(&A31)
A D32 2Y 0O 8 20EDTCDE(Z)
A DSPATR(&A32)
A D33 2Y 0O 8 26EDTCDE(Z)
A DSPATR(&A33)
A D34 2Y 0O 8 32EDTCDE(Z)
A DSPATR(&A34)
A D35 2Y 0O 8 38EDTCDE(Z)
A DSPATR(&A35)
A D36 2Y 0O 9 2EDTCDE(Z)
A DSPATR(&A36)
A D37 2Y 0O 9 8EDTCDE(Z)
A DSPATR(&A37)
A D38 2Y 0O 9 14EDTCDE(Z)
A DSPATR(&A38)
A D39 2Y 0O 9 20EDTCDE(Z)
A DSPATR(&A39)
A D40 2Y 0O 9 26EDTCDE(Z)
A DSPATR(&A40)
A D41 2Y 0O 9 32EDTCDE(Z)
A DSPATR(&A41)
A D42 2Y 0O 9 38EDTCDE(Z)
A DSPATR(&A42)
A PBCMDKEY 2Y 0B 11 2PSHBTNFLD((*GUTTER 2))
A PSHBTNCHC(1 'Today' CF10)
A PSHBTNCHC(2 'ThsMth' CF05)
A PSHBTNCHC(3 'Back' CA12)
A R ASSUME ASSUME
A 1 2' ' |
|
Display File Notes
Note that the code above shows a bolded 1 and
2 in the left margin:
- These are the attribute fields. Notice their
length, data type, and usage; they are one-character program-to-system
fields.
- These are the day fields that will display on the screen. Look at the DSPATR
keyword. This is where the field is linked to the corresponding attribute
field.
RPG Source (PMTCAL)
**********************************************************************
* Program........: PMTCAL *
* Programmer.....: Doug Eckersley *
* Date...........: 09/2003 *
* *
* Description....: Date Prompter *
* *
*====================================================================*
* Modification Log *
* *
* Date Req Who Description *
* ---------- ---- -------- ----------------------------------------- *
* *
**********************************************************************
H dftactgrp(*no) option(*nodebugio) datfmt(*iso)
FPMTCALD CF E WORKSTN INFDS(ScreenInfo)
D*=====================================================================
D* Parms
D*=====================================================================
D outRes S 1S 0
D outDate S D
D outWD S N
D*=====================================================================
D* Constants
D*=====================================================================
D*=====================================================================
D* Program Status Data Structure
D*=====================================================================
1 D***/copy DSDS
D SDS
D #Proc *PROC
D #Status *STATUS
D #PStatus 5S 0
D #LineNbr 8A
D #Routine *ROUTINE
D #Parms *PARMS
D #Exception 7A
D #ExcTyp 3A OVERLAY(#Exception)
D #ExcNbr 4A OVERLAY(#Exception:4)
D #MsgWork 51 80A
D #Lib 81 90A
D #MsgData 91 170A
D #File 175 184A
D #JobName 244 253A
D #User 254 263A
D #JobNbr 264 269S 0
D #ProcProg 334 343A
D #ProcMod 344 353A
D #LineNbr2 354 355I 0
D*---------------------------------------------------------------
D* Other Useful Stuff
D*---------------------------------------------------------------
D TRUE C *ON
D FALSE C *OFF
D*---------------------------------------------------------------
D OK S N
D Once S N INZ(*ON)
D Forever S N INZ(*OFF)
D*---------------------------------------------------------------
1 D***/end-copy
D
D*=====================================================================
D* Screen Work Area
D*=====================================================================
D***/copy DSCREEN
D CurrScreen S 2S 0 inz(1)
D
D ScreenInfo DS
D CmdKey 369 369A
D
D*** Command Keys
D F3 C X'33'
D F5 C X'35'
D F7 C X'37'
D F8 C X'38'
D F10 C X'3A'
D F12 C X'3C'
D F19 C X'B7'
D F20 C X'B8'
D
2 D*** Attributes
D atr S 1A
D
D atrNormal C x'20'
D atrGRN C x'20'
D atrWHT C x'22'
D atrRED C x'28'
D atrTRQ C x'30'
D atrYLW C x'32'
D atrPNK C x'38'
D atrBLU C x'3A'
D atrND C x'27'
D
D atrRI C x'01'
D atrHI C x'02'
D atrUL C x'04'
D
D atrPR C x'80'
D*** *** Hi colors (do not use atrHI): WHT, YLW, BLU
D***/end-copy
D
D*IndicNames DS based(InPtr)
D
D* User Field Change Control
D S1 DS
D S1MONTH
D S1MONTHX 2A overlay(S1MONTH)
D S1YEAR
D S1YEARX 4A overlay(S1YEAR)
D P1 S like(S1)
D
3 D*--- Green
D aWorkDay S like(atr)
D*--- Pink, for weekends and holidays
D aNotWorkDay S like(atr)
D*=====================================================================
D* Screen Mapping
D*=====================================================================
4 D aD DS
D D01
|
|
D D42
D D like(D01) overlay(aD) dim(42)
D Da 2A overlay(aD) dim(42)
D
4 D aA DS
D A01
|
|
D A42
D A like(A01) overlay(aA) dim(42)
D DS
D CSRFLD
D CsrDay 2S 0 overlay(CSRFLD:2)
D*=====================================================================
D* Switches
D*=====================================================================
D Switches DS
D ErrFound N
D ChgFound N
D ForceChg N inz(*off)
D*=====================================================================
D* Other Work Areas
D*=====================================================================
D SysDate S D inz(*sys)
D
D wDate S D
D Dx S 2S 0
D Day S 2S 0
D
D MonthN S 9A dim(12) ctdata perrcd(1)
D*=====================================================================
D* Prototypes
D*=====================================================================
D Holiday S 10A
5 D IsWorkDay PR extpgm('ISWORKDAY')
D @Date D
D @WorkDay N
D***@Holiday like(Holiday)
D*=====================================================================
D* KLISTS
D*=====================================================================
C*****************************************************************
C*****************************************************************
C *entry plist
C parm outRes
C parm outDate
C parm outWD
/free
exsr $inzsr;
exsr $Main;
*inlr = *on;
// ================================================================
begsr $inzsr;
// ================================================================
clear SCRN1;
exsr $Parms;
6 aWorkDay = %bitor(atrPR:atrNormal);
aNotWorkDay = %bitor(atrPR:atrPNK);
exsr $S1Change;
exsr $S1Edit;
endsr;
// ================================================================
begsr $Parms;
// ================================================================
test(e) outDate;
if (%error)
or (outDate = *loval);
outDate = SysDate;
endif;
S1MONTH = %subdt(outDate:*m);
S1YEAR = %subdt(outDate:*y);
outRes = *zero;
endsr;
// ================================================================
begsr $Main;
// ================================================================
dow CurrScreen > *ZERO;
select;
when CurrScreen = 1;
exsr $Screen1;
endsl;
enddo;
endsr;
// ================================================================
begsr $Screen1;
// Main Screen
// ================================================================
exsr $S1Show;
if (CmdKey = F3)
or (CmdKey = F12);
CurrScreen = *zero;
leavesr;
endif;
if (CmdKey = F5);
exsr $ThisMonth;
endif;
if (CmdKey = F10);
exsr $Today;
leavesr;
endif;
if (CmdKey = F7);
exsr $S1DecrMonth;
endif;
if (CmdKey = F8);
exsr $S1IncrMonth;
endif;
if (CmdKey = F19);
exsr $S1DecrYear;
endif;
if (CmdKey = F20);
exsr $S1IncrYear;
endif;
exsr $S1Change;
if ErrFound OR ChgFound;
exsr $S1Edit;
leavesr;
endif;
exsr $Process;
endsr;
// ================================================================
begsr $S1Show;
// ================================================================
exfmt SCRN1;
endsr;
// ================================================================
begsr $ThisMonth;
// ================================================================
S1MONTH = %subdt(SysDate:*m);
S1YEAR = %subdt(SysDate:*y);
ForceChg = TRUE;
endsr;
// ================================================================
begsr $Today;
// ================================================================
exsr $ThisMonth;
exsr $S1Load;
for Dx = 1 to %elem(A);
7 if %bitand(A(Dx):atrRI) = atrRI;
leave;
endif;
endfor;
CSRFLD = 'D' + %editc(Dx:'X');
exsr $Process;
endsr;
// ================================================================
begsr $S1DecrMonth;
// ================================================================
S1MONTH -= 1;
if S1MONTH <= *zero;
S1MONTH = 12;
exsr $S1DecrYear;
endif;
endsr;
// ================================================================
begsr $S1IncrMonth;
// ================================================================
S1MONTH += 1;
if S1MONTH > 12;
S1MONTH = 1;
exsr $S1IncrYear;
endif;
endsr;
// ================================================================
begsr $S1DecrYear;
// ================================================================
S1YEAR -= 1;
if S1YEAR <= *zero;
S1YEAR = 9999;
endif;
endsr;
// ================================================================
begsr $S1IncrYear;
// ================================================================
if S1YEAR < 9999;
S1YEAR += 1;
else;
S1YEAR = 1;
endif;
endsr;
// ================================================================
begsr $S1Change;
// ================================================================
ChgFound = ForceChg OR (S1 <> P1);
ForceChg = FALSE;
P1 = S1;
endsr;
// ================================================================
begsr $S1Edit;
// ================================================================
ErrFound = FALSE;
dou Once;
if S1MONTH <= *zero;
exsr $S1DecrMonth;
endif;
if S1MONTH > 12;
exsr $S1IncrMonth;
endif;
if S1YEAR <= *zero;
exsr $S1DecrYear;
endif;
S1MONTHN = MonthN(S1MONTH);
enddo;
// --------------------------------
if NOT ErrFound;
exsr $S1Load;
endif;
endsr;
// ================================================================
begsr $S1Load;
// ================================================================
D(*) = *zero;
8 A(*) = aWorkDay;
wDate = %date((S1YEARX+S1MONTHX+'01'):*iso0);
Dx = %rem(%diff(wDate:d'0001-01-07':*d) : 7);
Day = *zero;
dow %subdt(wDate:*m) = S1MONTH;
Dx += 1;
Day += 1;
D(Dx) = Day;
IsWorkDay(wDate:OK);
if (not OK);
8 A(Dx) = aNotWorkDay;
endif;
if (wDate = SysDate);
8 A(Dx) = %bitor(A(Dx):atrRI);
endif;
wDate += %days(1);
enddo;
endsr;
// ================================================================
begsr $Process;
// ================================================================
if %subst(CSRFLD:1:1) <> 'D';
leavesr;
endif;
if (D(CsrDay) <= *zero);
leavesr;
endif;
outDate = %date((S1YEARX+S1MONTHX+Da(CsrDay)):*iso0);
outRes = %rem(%diff(outDate:d'0001-01-01':*d) : 7) + 1;
if (%parms >= 3);
9 outWD = (%bitand(A(CsrDay):aWorkDay) = aWorkDay);
endif;
CurrScreen = *zero;
endsr;
/end-free
**ctdata MonthN
January
February
March
April
May
June
July
August
September
October
November
December |
|
RPG Program Notes
Again, refer to the bolded numbers in the left
column:
- The code between D***/copy and D***/end-copy are normally in a
copybook.
- These are the hexadecimal named constants for the various display
attributes. We will create an attribute value by bit manipulation on a color, an
effect, and the protect attribute. The color is required, and the effect is
optional. The protect attribute is only required for output fields, or
input-capable fields you want to protect. There is also a one-character
attribute work field defined here.
- These are fields that will contain default attribute values. They are
defined LIKE the attribute work field. One, aWorkDay, will be used for the
workday, and the other, aNotWorkDay, will be used for non-workdays.
- The screen day display fields and the corresponding attribute fields are put
into an array for easier manipulation. You will need to enter the rest.
- You will need to substitute your shop's program to determine if a date is a
workday.
- Here is where we establish the default attributes. The day display fields
are output, so the protect attribute is required. We also establish the
appropriate color for each: green (normal) for workdays, pink for non-workdays.
These attributes are set using bit manipulation with %bitor.
- This subroutine is looking for today among the day fields, by looping
through the array of attribute fields. It knows today has been found when the
reverse image attribute has been found to be on. %bitand is used to determine if
the reverse image attribute is on in the attribute field.
- This is where we assign the attribute value to the attribute field. We start
by defaulting all the attribute fields to aWorkDay (green). Note that this is a
simple assignment. As we loop through, building the calendar, we see if the day
is a workday. If it isn't, we assign aNotWorkDay (pink). Again, this is a simple
assignment. Also in this loop, if the day is today, we turn on the reverse image
effect in the attribute field. %bitor is used, because we are combining reverse
image with what is already in the attribute field--protected, green or
pink.
- This is another check to see if an attribute is on. In this case, we are
checking to see if the attribute value in aWorkDay is on.
Other Points to Consider
Attribute fields are ideal for use in subfile
records, as there would be no worry about keeping track of attribute indicators,
either in the record or in the indicator area.
These same attribute
values can be embedded in character fields to allow mixing display attributes
(including the non-display attribute) in one field.
In our quest to
reduce indicator usage, the use of attribute fields goes a long way toward that
goal. They are simple to use, and deriving the attribute values is also simple
with bit manipulation. Attribute values are also invaluable when needing to mix
attributes in a field, which cannot be done with indicators.
Give them a
try.
Doug Eckersley is the iSeries programmer
with a premier homebuilder in Columbus. He has been programming on the iSeries
for 10 years and has been in the business for 15. He is certified by
IBM.
LATEST COMMENTS
MC Press Online