OK, all you green-screen programmers out there, listen up! Today, we're going to discuss the dark cloud that hangs over all of your text-based applications. You know the ones I mean. They're the ones that rain broken words all across an otherwise-professional-looking program. When this storm breaks, it can destroy the appearance of a screen or document and cause the entire context of a phrase to change. For example, the sentence, "You are a doggedly persistent employee" could easily become, "You are a dog" when a word is broken inappropriately across two lines. Imagine reading that on your annual review!
This is the sort of thing PC programmers easily avoid by using common word wrap routines in their applications, something you probably thought would never be available to you. However, no longer is the word wrapping function confined to the PC world. Now, it is available for use with 5250 sessions, too.
This utility can be called from any application in which you need to equip a user with the ability to enter free-form text. When the word wrapper runs, it overlays your application's screens with a window that contains a 10-line subfile. If you pass a character literal to the program for the title, it is centered in the top of the window. At the bottom of the window, a counter keeps track of the current cursor page and line position.
With this function, your users can do real heads-down text entry, since they no longer need to worry about unnatural word breaks or need to press the field exit or Enter key to move to the next line. When they have filled 10 subfile lines, the screen automatically rolls to the next page, positioning the cursor correctly, even if the word wrapper must take the last word typed from the previous page and place it as the first word on the new page. When the user is finished with text entry, he simply presses the F3 key to end the program, which passes the formatted text back to the calling application. From there, your program can load the contents of the returned array into a database file, using the numeric counter passed back to your program to determine the number of times to loop through it. Now, you can print this data on a report or display it on a screen, and it will be perfectly formatted every time.
This utility consists of CLP program WW000C1 (Figure 1), display file WW000D1 (Figure 2), and RPG program WW000R1 (Figure 3). CLP program WW000C1 is included here merely to demonstrate how to call the RPG program and to provide you with a working example that you can use to try out the tool before you integrate it into your applications. In actual practice, you would probably embed the call to WW000R1 within another RPG or COBOL program.
When your application calls the word wrapper program WW000R1 (Figure 3), it will need to pass the program three parameters: an array 50 bytes long with 150 elements, a character string with a length of 56 characters, and a 15-digit packed-decimal field with five decimal places. The first parameter can be empty, or it can be loaded, prior to calling the word wrapper tool, with previously entered text if you want that text to be redisplayed. The second parameter can contain any text you want centered at the top of the word wrapper window to identify the content of the text being typed or displayed. The third parameter is used two ways: If this is the first call to the word wrapper from your application and the first parameter is empty, this field should be passed as a value of 1. If you are passing text to the word wrapper from some previous call, this field will contain the number of elements you are passing to the word wrapper program. In addition, when control returns to your program, this numeric field will contain the number of entries in the first parameter.
Some very ordinary housekeeping performed at program initiation performs the following:
o Centers any header text passed to it
o Clears and loads the subfile with any previously wrapped text you may have passed to this program
o Sets the cursor position in the footer of the word wrapper window
o Positions the cursor to the first row and column of the subfile window
Your user is presented with a window, overlaying the previous application, that contains a centered header that describes the content of the text he will type. In addition, if he has previously keyed anything for this particular record, that text will also be shown. If not, he will see 10 blank subfile lines upon which he may begin to enter data.
The CHECK(LC) and CHECK(ER) keywords are used on the subfile lines to allow the use of lowercase character entry and automatic record advance. When the cursor comes to the end of the line, that subfile line is read and its contents moved to the corresponding element of the input/output array TXT. It is at this point that the line is scrutinized to see if word wrapping should be performed.
The logic used to perform the word wrapping function is quite simple. It consists of some very ordinary RPG string operations. When subroutine WRDWRP begins, it initializes some work variables, including clearing array FTX. This array is used as a work area to temporarily store the formatted text.
The first test is to make sure that the current subfile line read is not blank. If it is empty, the program bypasses the word wrapping logic entirely. Otherwise, the input/output array TXT is moved to work array FTX. The value in the FTX element that is equal to the currently active
subfile line is moved to work variable STRING, which is the same length as the subfile line. The program then clears the FTX array element it just moved to STRING so that the FTX array can later be overwritten with the formatted data.
The last blank position in STRING is located by using the CHEKR op code, and that value is stored in R. I then search for the last nonblank position and store that value in variable LSTCHR. If the last nonblank character found is equal to the values of RMINS1 or ROWLEN, I want to reset the value in LSTCHR to be ROWLEN minus the position of the last blank position. This step is required to ensure that the word wraps properly when it begins in one of the last two positions on the line.
If there are characters at the end of the line, they need to be moved, or wrapped, to the next line. This is done by substringing the characters in variable STRING, beginning at the first position of the line and extending to the last blank position. (This is the reason for locating the last blank position earlier.) This substringed value is placed in work array FTX at the current element. If any characters did not get moved to FTX in that last operation, they are substringed into the first position of the next element of FTX. The number of characters to substring into that element is determined by subtracting from ROWLEN the value of the position of the last blank position from the previous line (the length of the subfile line). This formula gives the length of the number of characters to substring. That value is then subtracted from the value in RPLUS1 (the length of the subfile line plus 1). This is the position in variable STRING in which the characters that need to be moved to the next line begin. This information is then used to substring those characters into FTX at the next element. Finally, array FTX is moved to array TXT.
All that's left to do is build the footer literal to show the current cursor page and line position and to reposition the cursor and subfile page. The first part is accomplished by dividing the current active subfile line number by 10 (size of the subfile page) and adding 1 to it to get the next line number. The subfile record number is also incremented at this point. This new line number is then used in subroutine CURPOS along with some simple concatenation to build the footer literal.
Determining the next cursor position and positioning it to the correct subfile page is achieved by evaluating the current active subfile line. The line number is moved to a one-position numeric variable. If this value is 0, this is the last line of the 10-line subfile, so the cursor should be positioned on the first line of the next page. If it is anything other than 0, the cursor row position is incremented by 1. This positions the cursor on the correct line. To move it to the correct column, I add the value in LSTCHR (where I stored the last nonblank position of the previous line) to RELCOL (the relative position on the screen of the beginning of the subfile line) to get the new column number. This positions the cursor on the next column after the last character typed, even if that character was the last character on the previous line.
There are two final things to be aware of: First, the display file for the word wrapper window should be compiled with RSTDSP(*YES). You may or may not need to do the same with the display used by the calling application. You'll have to experiment with that.
Second, you should have the Typeahead (keyboard buffering) function turned on. Keyboard buffering can take one of two forms: *TYPEAHEAD (which tells the system to turn on the keyboard type ahead feature but not the Attention Key buffering) or *YES (which turns on both).
You can turn the function on by one of three methods:
o For the entire system, set system value QKBDBUF, specifying either *TYPEAHEAD or *YES.
o For individual user profiles, use the Change User Profile (CHGUSRPRF) command, setting parameter KBDBUF to either *TYPE- AHEAD or *YES.
o For the job level, call API QWSSETWS, passing it a parameter of 1 (which is the same as *TYPEAHEAD in the other options) or 2 (which is the same as *YES in the other options).
You can modify the size of the values used in this program. However, if you decide to change these values, you should be aware of the following:
Any reference to the length of the subfile line, which is currently 50, will need to be modified if you change it. This includes the arrays TXT, FTX, and variable STRING. For convenience, variables ROWLEN, RPLUS1, and RMINS1 are initialized with the values of 50, 51, and 49 in the INIT subroutine and then referenced throughout the text to determine row length, row length plus one, and row length minus one.
If you increase or decrease the number of elements in the input/output array TXT, you will also need to reflect those changes in array FTX, as well as change the subfile size parameter in the accompanying DDS. You should also be aware that the logic for determining the next cursor row is based on a 10-line subfile. If you change the size of the subfile page, you must modify this area as well.
If you change the column or row where the subfile is displayed, you must modify variables RELROW and RELCOL. Currently, these values are initialized at 11 and 15 in the INIT subroutine.
Indicator 03 is an arbitrary choice on my part to use for updating the text and closing out the program. You could easily expand this functionality by adding separate indicators for updating, deleting, and exiting.
That's it! Wasn't that simple? OK then, it's time for you to get this tool into production and start basking in the praise that will be heaped upon you by a multitude of satisfied users. You will no longer have to hide in your cubicle because your users' text looks like someone chopped it up with scissors. Now, you can finally walk to the break room for coffee with your head held high, instead of waiting till everyone else in the building has gone to a meeting.
Figure 1: CLP program WW000C1 shows how to pass the 550 X 50 byte array and title to the utility
/* To compile: */
/* CRTCLPGM PGM(XXX/WW000C1) SRCFILE(XXX/QCLSRC) */
/* This CL is used as an example of how to call the Wordwrap Tool. */
DCL VAR(&TXT) TYPE(*CHAR) LEN(7500)
DCL VAR(&HDRLIT) TYPE(*CHAR) LEN(56) VALUE('Word +
DCL VAR(&CNTR) TYPE(*DEC) LEN(15 5) VALUE(1)
CALL PGM(WW000R1) PARM(&TXT &HDRLIT &CNTR)
A* To Compile:
A* CRTDSPF FILE(XXX/WW000D1) SRCFILE(XXX/QDDSSRC) RSTDSP(*YES)
A DSPSIZ(24 80 *DS3)
A CF03(03 'EXIT/SAVE')
A R S1SFL SFL
A TEXT 50A B 11 15CHECK(ER)
A11 12' '
A11 67' '
A R C1SFL SFLCTL(S1SFL)
A 82 SFLDSP
A 81 SFLDSPCTL
A 80 SFLINZ
A CSRLOC(XROW XCOL)
A RECPOS 4S 0H SFLRCDNBR(CURSOR)
AXROW 3S 0H
AXCOL 3S 0H
ASRRN 5S 0H
A HDRTXT 56 O 10 12DSPATR(RI)
A R C1FTR
A FTRTXT 56 O 21 12DSPATR(RI)
A R DUMMY
A DMYFLD 1A B 2 2DSPATR(PR) *****************************************************************
* To Compile: CRTRPGPGM PGM(XXX/WW000R1) SRCFILE(XXX/QRPGSRC)
FWW000D1 CF E WORKSTN
F RRN KSFILE S1SFL
*E TXT 150 50 Input Text To FMT
E FTX 150 50 Formatted Text
E HC 56 1 Header Text Cntrd
E HU 56 1 Header Txt Uncntd
*I 'ABCDEFGHIJKLMNOPQRS- C VC