Building on last month's basics.
by Craig Pelkie
Last month, we covered some basics of data stream programming and became familiar with using RPG bit manipulation opcodes. Now it's time to expand our knowledge, and create useful techniques. In this article, you'll see an example of how a text "window" is created, and how you can dynamically position it within a program.
Most of this article describes the example programs, and points out where you can customize them to your own needs. We'll conclude with some of my thoughts on the idea of beautifying "nonprogrammable" workstation displays.
The Object of this Exercise...It frequently happens, in interactive programs, that you want to display "help" text, or perhaps a list of codes or other prompting information. Typically, this information would be displayed on another display format. Using standard programming techniques, the alternate format would appear above or below the part of the display that needs the expanded informations. In the case of IBM-supplied HELP screens, an entire new display is presented.
This is in contrast to typical PC applications, where "windows" of various natures are the expected technique. There are many variants of windows, such as pop up, drop down, exploding, imploding, and so on. You can see many examples of these windows in practically any modern PC program. If you don't have access to a PC, a quick look through any of the PC magazines will furnish several examples.
Forgetting for now the glamour, we can focus on techniques that are beneficial for our users. One of the most useful types of windows, from their point of view, is the pop up window that appears near the item in question. For example, you might have a pop up that displays state or province abbreviations near an address entry area. You could provide brief prompting or help text for any item on a display.
To reiterate, one of the problems of "standard" System/36 S & D or AS/400 DDS specs is that your "window" (which is really an overlay display) is hard-coded into the display file. That means that every time you display it, it will be in the same location. As long as you don't overlay the current item of interest on the user display, that technique is perfectly acceptable. However, it is disconcerting to most people to overlay the item of interest. It is more useful to determine an acceptable overlay position and put the pop up window there. Using data stream programming, you can control the location and size of a window within your program. It is nowhere near as easy to program dynamic windows, but the trend now is to favor the user at the expense of the program, so you might wish to investigate data stream programming to the point where you can incorporate it into your toolbox.
Some Notes on the Program
First, let me point out that these example programs are intended solely for pedagogical purposes. That is my way of saying that the intention is to demonstrate data stream programming, nothing more. All of the additional code is just so that you will have something interesting to look at on the sample display. In all likelihood, the programs can be improved for efficiency, reliability, readability, etc.
On the other hand, I have gone to great lengths to strip away as much nonessential code as possible. For example, it is unlikely that you would store help text in a compile time array, but it lets us examine the program without need for a data or message file. So don't become overly concerned with the example per se; I will point out to you where you would insert your own code.
It may help if you read the examples and think of them as a subroutine or subprogram that you would call. After all, the point of learning how to create a pop up window is so that you can use it in any number of programs. RPG/III or RPG/400 programmers have the advantage here, since they can further develop the program into a general purpose callable module. RPG/II programmers will have to work at developing the program into /COPY members or an include module, and should consider this as another example why they should get an RPG/III or equivalent compiler.
Also, there is no reason to expect that these programs will work with any of the PC RPG compilers. PC compilers have no need to use the 5250 data stream, since they can interact with the screen through DOS or directly. On the other hand, data stream programming does work with PCs used for 5250 emulation. Although I have not had experience with other types of devices, I would suspect these techniques work with ASCII displays. If anybody has these types of displays and tries these examples, it would certainly be appreciated if you would let us know the results of your experiments.
There are two programs and display files provided, one for the S/36, the other for the S/38 and AS/400. You will need access to the #CVTTB (Convert to Binary) subroutine presented last month; that is referenced as a /COPY module. Refer to the sidebar for instructions on creating the sample programs.
The Scenario
The example programs display a screen, as shown in 1. This screen is neither complex nor interesting; it merely has three "generic" fields and row/column fields.
The example programs display a screen, as shown in Figure 1. This screen is neither complex nor interesting; it merely has three "generic" fields and row/column fields.
To demonstrate pop up windows, type a "?" in the first position of field 1 or field 2 to display a text window. If the row and column are zero, or if they have invalid values, the window is displayed starting in row 5, column 43. The text window is made up of top, side and bottom borders. Within the window, there are six lines of 30 characters available; text is neatly formatted within the window. When you press ENTER, the original display is refreshed. 2 shows an example of the text window in the default position.
To demonstrate pop up windows, type a "?" in the first position of field 1 or field 2 to display a text window. If the row and column are zero, or if they have invalid values, the window is displayed starting in row 5, column 43. The text window is made up of top, side and bottom borders. Within the window, there are six lines of 30 characters available; text is neatly formatted within the window. When you press ENTER, the original display is refreshed. Figure 2 shows an example of the text window in the default position.
The Good Stuff
Surprisingly, the interesting part of the program isn't in the C-specs, but rather in the data structures, particularly the "WTDLN" data structure. This DS contains all of the formatting information that we need for each row in a window. As promised last month, a window is nothing more than several data stream commands appended to each other and presented at one time. Each command is composed with the WTDLN Data Structure; these commands are appended in the array WTD.
First, refer to 3. This shows you the composition of a text line, with borders, for 30 characters of text. To format such a line, we need 39 characters: three for 5250 data stream commands, and three each for the left and right borders.
First, refer to Figure 3. This shows you the composition of a text line, with borders, for 30 characters of text. To format such a line, we need 39 characters: three for 5250 data stream commands, and three each for the left and right borders.
The first position of our Data Structure is the Write to Display command (binary value x'11'). This is immediately followed by the row and column settings, which are encoded as one byte binary values. For a pop up window, the column value is "constant," so that the left side of the window starts in the same position on succeeding rows. The row value varies. In our example, we are formatting a window that is eight rows deep: a top and bottom border, and six text lines. The row values are incremented by one in each successive usage of the WTDLN DS. I've never really thought about it, but I guess there's no reason why you can't use decreasing row values in successive WTD commands; the result should be the same.In our example, a side border is a one position reverse image character. A top or bottom border is a 34 character reverse image field, aligned over the left and right borders. To create a side border, you code a start attribute and end attribute for each border; this is shown in WTDLN as fields WDSTA1, WDENA1, WDSTA2 and WDENA2. (The fields WDSPC1 and WDSPC2 are not really necessary, but show you where in the data stream the "space" is.) A top or bottom border is composed of one start and one end attribute, in this case, WDSTA1 and WDENA2. By not including any intervening attributes, the setting of WDSTA1 is continued across to WDENA2.
The "user area" of our window is in the WDTXT field. This is where the text "goes," although there is no reason why you can't place it anywhere between the start and end attributes, WDSTA1 and WDENA2.
One After Another
Formatting our window, then, is a matter of creating a top border, six lines of formatted text, and a bottom border. As each piece of the window is formatted, we'll place it into a hold area, pending the display of the window. The hold area is defined within the second DS, WTDSP. This data structure starts with several control characters, followed by the WTD array. We need to reserve 312 characters for our window (8 lines with 39 characters), plus six bytes for control characters, so the work array is 318 bytes. The S/38 and AS/400 crowd also need five preceding control bytes. Given the bad time I had debugging the S/36 version, and the ease of getting the AS/400 version to work, I have no problems dealing with five additional bytes.
Each time we complete formatting a window row in Data Structure WTDLN, we store that part of the window in WTD. A simple increment is used to set the next "store at" location. Believe me, the consequence of accidentally overlapping the WTDLN rows is not a window.
Into the Code!
Now that we know what to expect, let's work through it.
Go first to the #INIT routine. As in last month's article, this routine is mostly bit setting and field moving. Using the convention described last month, hex fields are set into "X" fields. Verify the bit settings! S/38 and AS/400 programmers have a bit of extra work to do, setting the send length, receive length and requested function fields. Note that the send and receive lengths are two byte binary fields, so you have to set each half of the field. We are sending, so we set the send length to 318; we don't include the send/receive length and requested function byte within the send length total. We aren't receiving, so receive length gets set to x'0000'. The requested function is the SEND function, x'71'.
The main routine is not especially interesting; it merely displays the main screen and determines if we typed "?" in the first position of Field 1 or Field 2. If we did, the window formatting routine, #FMHLP, is called. One way that you might increase the usefulness of this routine is by using the HELP key and a cursor position routine to determine where the cursor is when HELP is pressed. With that information, you can have your program determine which help should be displayed. In our simple example, we select either of two helps by the setting of $FLDNO, the field number.
The #FMHLP (Format HELP) routine is a series of calls to other subroutines and display operations. The reason for calling the other subroutines is to make clear where you can provide your own customizations.
For example, the #GETTX (Get Text) routine is nothing more than moving compile-time array text to a work array. You probably won't compile HELP text into a program; it would be much more useful to use a message file or a data base file of messages. Using either of those methods, you would obviously supply retrieval coding in this routine.
The #FMTLN (Format Line) subroutine is used to break up the text into lines of approximately 30 characters each. Again, don't quibble with this routine; the purpose is to simply transform a "long" piece of text into shorter chunks that are used in the window. For normal text, as in the examples, the routine works fine; you can probably make it fail by including stupendously lengthy phraseology in your text. If you break it, you've got to make it work, though. The point is, you can do anything you want inside this routine to break up text into pieces to use in the window. The pieces are stored in the D30 (display 30 characters) work array. Now stop and think about this. The text that you display could, possibly, be a list of codes from one of your database files. So, if you read six codes from the file, you can store them in D30. Maybe you can think of some other possibilities of things you'd like to include in a pop up window. Consider using the prototype method shown here: get and format your information before worrying about putting it into the data stream. When you get to the point of formatting the data stream, you'll be busy enough with that, so do your up front work up front.
Programmed Intelligence
One of the more interesting modifications that you should investigate is in the #SETRC routine, set row/column. As shown in this example, the row and column settings are checked for validity, since invalid settings create invalid results, with system-type messages. If you are using a 27x132 display, you can get away with more. Midrange Computing has, in the past, published routines that you can use to determine the type of terminal a program is using; you may want to incorporate these routines into a window builder.
As previously stated, the column position is going to be constant for a window, so that is converted to binary in this routine.
Now, what can you do with this routine? Within the routine, you set the location of the window, in terms of its top left corner. The width and depth of the window are determined by how you format the data stream. In our example, we have hard coded a width of 30 characters, with two borders of three positions each. The depth is set at eight lines.
Knowing the width and depth of the window, and knowing the current cursor position when control is passed to the window builder, you can calculate a suitable top left corner for the window. The factors that you want to consider are that the window should not overlay the field "of interest," and should not extend beyond a horizontal or vertical physical boundary. In our example program, displaying too close to the bottom of the display results in a shallowf window. For example, if you try to display eight lines starting at row 20, you will only get four lines in the window. On the other hand, displaying too close to the right margin causes the window to "wrap," which looks absolutely horrible. You can try this in the example program to see what I mean.
So you'll want to calculate a starting position that takes those factors into account. The advantage of calculating the starting position is that you don't have to "hard code" this, either in the program code or as part of the data (for example, setting a starting position as part of the information retrieved for a field). By not hard coding, you can freely change the display, adding or moving fields as needed.
Alternatively, you might consider partitioning the display into "logical window areas." For example, you might define six areas, top left, middle, right, and bottom left, middle and right. You could then set the six starting values, and depending upon the retrieved cursor position, choose one of the locations that causes the least conflict. This has the advantage of being simpler to code, and still avoids hard coding a window location with a field.
The example program lets you change the row and column settings and immediately observe the effect, so you might get some ideas by running it. This is really an area that you should think about. Positioning the window must be done carefully so that it is "useful," and not a mere special effect.
The Big One
What everybody thinks is the big one is really just a small part of the program. I'm referring to #FMWTD, format the WTD array. At this point, we've set all the initial bit values, we've retrieved the text information to display, and we've determined what the starting position of the window is. Putting it all together is a matter of formatting our WTD array, which is a series of bytes, which is a... stream! One bit after another, marching out the twinax port, to become beautiful, inspiring displays...
Well, not so fast. There's still plenty of time to create unrecoverable I/O errors, and this is practically the worst time, since everything should be well set by now. So let's take the slow tour, and make sure we understand all the players before starting the game. Preparation is everything.
First of all, there is the business of setting some control bytes. AS/400 - S/38 folks, this is in addition to the aforementioned five bytes that you have to worry about. Everybody's in at this point. The first control byte is the "escape" character, x'04' (WDESC1). This is the one that tells the workstation controller that we're in charge, muscling aside the comparatively wimpy S & D/DDS stuff. On the other hand, you'd better know what you're doing from this point out.
The next byte is WDCFT, x'50', Clear Format Table. According to the book (5250 Functions Reference), this "...clears the format table but does not affect the present display." The book goes on to explain what this entails, none of which is relevant to this discussion. Clear Format Table is used in contrast to Clear Unit (x'40') which clears the display. I must confess I forgot to include WDCFT in my first pass at the AS/400 version, and it didn't cause any ill effect, whereas the S/36 version misbehaved badly.
WDCFT is followed by another escape byte (WDESC2). This prefaces the Write to Display command, WDWTD (x'11'). This is where we get down to business and finally tell the system what we're up to: we're going to be formatting the display from this point. Two bytes accompany this command, Control Character 1 and 2. Control Character 1 is relevant if you are programming I/O fields in the data stream; since we aren't, we clear this field. Remember that "clearing" a binary field means setting it to x'00', not blank, since blank is x'40'. Control Character 2 is set to x'08'; this indicates that the keyboard should be unlocked at the completion of the data stream.
The WTD "template" is nothing more than constants in each row of the window. These constants are in the WTDLN data structure, and include the Set Buffer Address order (x'11', WDSBA), the initial starting attribute (WDSTA1), set here as reverse image, and the final ending attribute (WDENA2) set as "normal."
We next set the starting position in the WTD array, where we're going to store our formatted window rows. This is position 7, since the first six positions are taken up with the preceding control bytes.
Ready, Set, Go!
Formatting the WTD array is done with a simple loop. In our example, we format eight lines, so the loop is done eight times. The row is set to a binary value, placed in the WTDLN data structure, then incremented if it's not going off the bottom of the display.
The "user area" of the window line is cleared, with field WDCLR. This simply redefines all of the positions in WTDLN that are between the first starting attribute and last ending attribute. Recall that this is how a top or bottom border is formatted: a starting attribute with no intervening attributes, terminated at the far end with the ending attribute.
If we are on window lines 2 through 7, we get a bit more involved. We set the first ending attribute, to create the left border, place the saved text, and set the second starting attribute to create the right border.
Now that the window row is formatted, we store it in the WTD array. We then increment the WTD position so that the next row will be stored immediately following.
Once this is all done, we're back in #FMHLP.
On the S/36, we simply display our WTDFM format, which displays the window. AS/400 and S/38 displays the WTDFM, then EXFMT another small format, so that the window will stay on the display until we press ENTER.
Embellishment
There are a couple of things that you might want to try with this example. If you have a color terminal you'll have a lot more fun, but even the green or amber people can play along.
One of the first things you might want to try is varying the starting attribute. You have many choices here; just find an attribute byte within the range x'20' through x'3F' and try it. This is where color terminals really show off, since you can use the reverse image attributes for colors and create spectacular effects with the borders. An especially pleasing effect is to set the top border to one color, and set the side and bottom borders to another.
Another interesting twist involves setting the attribute for the text information. In this example, the setting of WDENA1 (ending attribute 1) is actually the attribute that controls how the text is displayed. Since we have set this to x'20', you get the "normal" display. Go ahead, live a little... set it to something else. Red borders with white text? Ooooh! I like it!
One little problem we can't get around, though, is setting two "solid" colors next to each other, since the attribute intervenes. That is, you can't set a red border followed immediately by a reverse image white field; there will be a blank "stripe" running the height of the window where the attribute byte lives. Quite annoying; you'll have to try the setting and judge for yourself if it's acceptable.
Another alternative is just setting the whole window to a solid. You know how to do this; just look at how the top or bottom border are set. All you code is the first starting attribute and the last ending attribute. By not including intervening attributes, the setting from the first starting attribute is continued across the window row.
Also, there is nothing to say that you can't include text within the top or bottom border. Typically, the top border text would be some sort of identification. For example, if you're displaying state codes, you might say "State Codes" within the top border. The bottom border is typically short instructions, such as "Press ENTER." All you have to do is provide the text.
Variation
As you become more familiar with these techniques, you will probably long to create windows other than 8 rows by 30 characters plus borders. It's not easy to explain, but what it will amount to is creating a different WTDLN equivalent, probably a work array, maybe 50 - 60 characters long -- long enough to accommodate your maximum window width. You will then be responsible for determining the position of the start and end attributes, and the location of your text. The first three positions will always be the SBA order, row and column.
You would then move your WTDLN equivalent to the WTD work array or equivalent. You can easily make WTD bigger to accomodate bigger windows; just change the array definition, data structure, and the field in the display file.
This brings up a problem, though. It took me a while to understand the problem and come up with a workable solution, so it might take you a while also. The problem is that the length of the data stream is pretty well set by the length of the WTD array. In our example, we are sending 318 bytes, and that's fine, as long as we're sending exactly that many bytes. If your window is only going to use up 200 bytes, though, you've got to do something with the extra 118 bytes. If you don't, those extra 118 bytes "run on" across the display after the window and onto the next line, creating an awful mess.
AS/400 and S/38 people have an easy out here, since they can simply calculate an appropriate Send Length and set that in the initial control byte (WDSND in our example). S/36 people are kind of stuck: we don't send anything telling it how long the stream is, so it takes the length of the field defined in the display file.
The work around that I have found is to send the excess bytes into a "dead area" on the display. For me, this amounts to sending the excess to line 24, column 1, and continuing for 78 bytes across the display. Got more than 78 bytes to deep six? No problem, just add another command starting at line 24, column 1 again. After all, there's nothing to say that you can't use the same row/column settings more than once per data stream. Granted, in most cases it would be counterproductive, but for getting extra blanks out of the way, it sure beats having them parade across the screen right after the window, mowing over everything in their path.
I freely admit that it is difficult to convey this problem, and most of you are probably shaking your heads at this point. But if any of you have been doing data stream programming and have another suggestion for this problem, please feel free to share your technique with us.
Multiples
After you become au fait with the windowing technique presented here, you will probably want to press on to new accomplishments. One of the areas that you might want to investigate is multiple, possibly overlapping windows. For example, you can display a window, then display another format, asking if you want more detail? If the answer is yes, you can format and display another window, even with the first window still on the display. For a really stunning effect, you can set the borders of the second window to a different color.
Unfortunately, this is like enjoying an exquisite meal in a fine restaurant, then finding something very bad in the soup. The problem is that there is no good way, within a high level language, to "back up." Anybody familiar with a PC program that uses overlapping or sequential windows knows that you can "pick off" windows one at a time, usually with the ESCAPE key. As you pick off each window, the underlying display is shown, as it was before the window covered it. You will probably not be cheered to learn that the 5250 protocol includes two commands, Save (Immediate) and Restore Screen (Immediate), that seem to perfectly suit this need, yet, are apparently not supported in HLL. You're familiar with the effect of these commands; for example, when you System Request out of one program to another, then come back to the original. Notice that everything on the original display is restored as it was, including things like the cursor position and field settings such as reverse image. Explicit requests to IBM for advice on these two vital commands was curtly unanswered. So the question remains, are these commands available in a HLL? My inability to use them does not mean they can't be used, but I sure knocked a lot of holes in the wall with my head, and haven't gotten anywhere.
So, the net result is that you can fill up your screen with all sorts of pretty windows, but it's kind of a one-way trip. When you or your users want to get back, you dump the whole thing at once, through the simple expedient of displaying the original format. This is what we show in our example. I suppose if you are unusually adventurous you could go to the trouble of determining what your window is overlaying, then redisplay that in a data stream command... no, forget it. That is really too much work, and your users will probably be just as happy to go straight back to the starting point.
Philosophy
The technical discussion for this series is about over, so the time has come to speak of many things. How will you use windowing techniques in your programs?
To start with, you should always remember that your user's enchantment will probably be short lived, especially if you make loud, intrusive windows. Always remember that users don't really care about a particular technique, and will probably never develop any appreciation for the technical difficulties that you've surmounted. And that is as it should be; in a way, the highest compliment that you will get is nothing. Being unobtrusive is the greatest goal. This is not some far-out nonsense, but rather an observation of how we deal with things. After all, when do you notice your power steering or brakes? Only when they don't work, that's when. The same applies to your users; the initial novelty of seeing these little pop ups quickly becomes part of their background noise, but ill-conceived windows will be sand in the gears of their daily work.
For example, we've already mentioned that it is desirable to have a window not cover the field in interest. In some cases, this is unavoidable, but you should give some thought to it.
Another area of rampant abuse is with color. I once had to work with a program where the author had 16 colors available, and wouldn't you know it, he thought it was just dandy to use a different one on every line. This was probably the most outstanding example I have seen of complete lack of awareness, but there are others. For example, on the 5292/3197 terminals that I have seen, the red and blue colors are very difficult to read when used for long text strings. Since you are the programmer, you are ostensibly in control of every part of your program and its presentation. So exercise that control for the good.
One of the considerations that you may have is compliance with some set of standards, notably SAA. Without taking on the whole draconian set of edicts associated with "nonprogrammable workstations" (dumb terminals to us), you will eventually have to decide how far you want to buy into it. My personal feeling is that dumb terminals will not be the focal point of any new efforts on IBM's part, so there's no particular requirement to think that they have any great insight into screen design within the limits of the dumb terminal (which includes the 5250 data stream). With some effort on your part, you can take the techniques shown in these articles and develop windowing modules that will enhance your programs far more than anything else. Understand, windowing is not a panacea for all of the inherent lacks of dumb terminals, but it is possible to create program interfaces that your users will find more useful.
References
In this series, the main reference has been the 5250 Functions Reference Manual (SA21-9247). My feeling is that you shouldn't try to go too much further without this manual. As previously mentioned, this is one of the better manuals I've seen from IBM, for any purpose. You can learn a lot quickly; the encyclopedia format encourages casual browsing.
In terms of screen design, it's time to move on from the Concepts and Programmer's Guide, and SDA manuals of old. IBM actually has two pretty decent SAA manuals, albeit with programmable terminals (PCs to you and me) in mind. These are Writing Applications: A Design Guide (SC26-4362), and Common User Access Advanced Interface Design Guide (SC26-4582). Of the two, I've found the Advanced Interface manual the more useful. There is a definite OS/2 slant in these books, although from what I've seen, MicroSoft Windows supplies all of the relevant features, and if you're ambitious, you can too, even on dumb terminals. And yes, I know that Apple Macs had the jump on everybody, and there are various publications telling about their ideas.
For general, all around knowledge, of windows, menus, commands, and just understanding how normal (non programmer) people interact with computers, I found Programming the User Interface by Brown and Cunningham extremely useful (John Wiley & Sons, 1989, ISBN 0-471-63843-9). This is one of those books where, if you use a highlighter, you'll have most of every page marked up. Myself, I keep my books pristine; after all, if everything's important, then nothing's more important than anything else. This book tells you why some colors are better than others; why certain arrangements on the display are better; how to use windows to help the user. I like it because it takes both the user's and programmer's point of view, but does not patronize the programmer in the manner that I have found in other references.
Sign off
This brief series has covered a lot of ground. There are many more techniques that you may wish to explore, for example, including I/O fields within the data stream. The best thing you can do is get the Functions Reference Manual, then create short example programs such as in these articles. Once you understand a technique, you can incorporate it into useful programs.
Because of the data stream's intolerance of error, you may feel at times that you are learning how to program all over again. Be thankful if you are on an AS/400 or S/38, since the formatted dumps will help you find errors quite quickly. Usually, the errors are bit settings, invalid row or column settings, or mistakenly overlaying parts of the data stream.
Getting this under your belt will make you and your users quite happy. I know, it isn't everything, but considering that some people sit in front of screens all day with this work facing them, any improvements that we can make are quite welcome.
Creating The Sample Programs
There are two sample programs with associated display files, one for the S/36, the other for the AS/400 and S/38 (figures 4 through 8). By following these instructions, you can create the samples from this article, or download the source from Midrange Computing's BBS or their Resource Library.
As mentioned in the article, you will need access to subroutine #CVTTB (Convert to Binary), presented in last month's article. This is shown as a /COPY module in these sample programs; you can use the /COPY or include the whole subroutine in your source code. If you use /COPY, you will have to use the AUTOC procedure or CRTRPTPGM command to compile the program.
RPG/III and RPG/400 programmers may wish to update the code to use compound IF statements, for example, in the #SETRC and #FMWTD subroutines.
S/36 Notes
For the S/36, simply create the display file WTDFMT from the sample S & D specs, then compile the sample program WTD2. When you have the screen and program, you can simply LOAD and RUN the program; no file specs are used.
S/38 and AS/400 Notes
As pointed out last month, creating S/38 and AS/400 versions is a bit more complicated. We have to "trick" the compiler into compiling with a "regular" display file, then at run time substitute a display file with the USRDFN keyword.
To compile the program, create the two display files, WTDFMT and WTDFMTX. Be sure to specify LVLCHK(*NO) on the CRTDSPF command. Once you have both display files, override the WTDFMT file to WTDFMTX, as follows:
OVRDSPF WTDFMT TOFILE(WTDFMTX)
Then use the CRTRPTPGM to compile program WTD2. When you compile the program, the display file override is in effect, so the program uses the definitions from file WTDFMTX. To run the program, delete the display file override, then call the program.
You can refer to last month's article for another example of this dual display file technique.
5250 Data Stream Programming
Figure 1 (displayed when program is executed)
5250 Data Stream Programming
Figure 2 (displayed when program is executed)
5250 Data Stream Programming
Figure 3 Composition of text line with borders
Figure 3 SA(1), SP(1), EA(1), (text), SA(2), SP(2), EA(2) SA(1) - start attribute 1 SP(1) - space 1 (the left border) EA(1) - end attribute 1 (also serves as start attribute for text) (text) - any alphanumeric data SA(2) - start attribute 2 SP(2) - space 2 (the right border) EA(2) - end attribute 2
5250 Data Stream Programming
Figure 4 RPG program WTD2
* WTD TEST PROGRAM FWTDFMT CF E WORKSTN E A31 31 1 Work array E D30 6 30 Display Text E F3 300 1 Long field E TX 150 1 Text work E WTD 318 1 WTD string E HTX 1 4 75 Test text IFIELD3 DS I 1 300 F3 I DS I*************************************************************** I* Data Structure WTDLN - WTD line I* I* WDSBA - SBA command x'11' I* WDROW - row (binary) I* WDCOL - column (binary) I* WDSTA1 - start attribute 1 I* WDSPC1 - space 1 I* WDENA1 - end attribute 1 I* WDTXT - display text I* WDSTA2 - start attribute 2 I* WDSPC2 - space 2 I* WDENA2 - end attribute 2 I* WDCLR - space to clear I*************************************************************** I 1 39 WTDLN I 1 1 WDSBA I 2 2 WDROW I 3 3 WDCOL I 4 4 WDSTA1 I 5 5 WDSPC1 I 6 6 WDENA1 I 7 36 WDTXT I 37 37 WDSTA2 I 38 38 WDSPC2 I 39 39 WDENA2 I 5 38 WDCLR IWTDFL DS I*************************************************************** I* Data Structure WTDSP - formatted WTD display I* I* WDSND - send length (binary) I* WDRCV - receive length (binary) I* WDRQFN - requested function (binary) I* WDESC1 - escape char 1, x'04' I* WDCFT - Clear Format Table, x'50' I* WDESC2 - escape char 2, x'04' I* WDWTD - WTD cmd, x'11' I* WDCC1 - control char 1 (binary) I* WDCC2 - control char 2 (binary) I* WTD - WTD work array I*************************************************************** I 1 2 WDSND I 3 4 WDRCV I 5 5 WDRQFN I 6 6 WDESC1 I 7 7 WDCFT I 8 8 WDESC2 I 9 9 WDWTD I 10 10 WDCC1 I 11 11 WDCC2 I 6 323 WTD C EXSR #INIT C* C* Display main format, check for "help" request C* C *IN03 DOUEQ'1' C EXFMTTESTFM C* C *IN03 IFEQ '0' C MOVELFIELD1 W01 1 C* C W01 IFEQ '?' C MOVEL'1' $FLDNO 1 *FieldNumber C EXSR #FMHLP C* C ELSE C MOVELFIELD2 W01 C* C W01 IFEQ '?' C MOVEL'2' $FLDNO *FieldNumber C EXSR #FMHLP C END C END C* C* ..non HELP processing here C* C END C END C/EJECT C*************************************************************** C* Program initialization C*************************************************************** C* C #INIT BEGSR C* *************** C* C SETON LR C* C BITOF'01234567'X00 1 C MOVELX00 X01 1 C MOVELX00 X04 1 C MOVELX00 X08 1 C MOVELX00 X11 1 C MOVELX00 X20 1 C MOVELX00 X21 1 C MOVELX00 X3E 1 C MOVELX00 X50 1 C MOVELX00 X71 1 C* C BITON'7' X01 C BITON'5' X04 C BITON'4' X08 C BITON'37' X11 C BITON'2' X20 C BITON'27' X21 C BITON'13' X50 C BITON'23456' X3E C BITON'1237' X71 1 C* C* ..set S/38, AS/400 specific controls C* WDSND - send length C* WDRCV - receive length C* WDRQFN - requested function C* C MOVELX01 WDSND *Set to 318 C MOVE X3E WDSND C* C MOVELX00 WDRCV *Init left C MOVE X00 WDRCV *Init right C* C MOVELX71 WDRQFN *Send function C* C ENDSR C/EJECT C*************************************************************** C* Format HELP text for selected field C*************************************************************** C* C #FMHLP BEGSR C* *************** C* C EXSR #GETTX C EXSR #FMTLN C EXSR #SETRC C EXSR #FMWTD C* C WRITEWTDFM C EXFMTENTFM C* C ENDSR C/EJECT C*************************************************************** C* Get HELP text for display C*************************************************************** C* C #GETTX BEGSR C* *************** C* C $FLDNO IFEQ '1' C MOVEAHTX,1 TX,1 C MOVEAHTX,2 TX,76 C* C ELSE C MOVEAHTX,3 TX,1 C MOVEAHTX,4 TX,76 C END C* C ENDSR C/EJECT C*************************************************************** C* Format text into 30 character lines C*************************************************************** C* C #FMTLN BEGSR C* *************** C* C Z-ADD0 K 50 *ArrayIndex C Z-ADD1 N 50 *ArrayIndex C* C N DOWLE150 C MOVEA*BLANKS A31 C MOVEATX,N A31 C Z-ADD31 J 50 *ArrayIndex C* C* ..check for word break around position 30 C* C A31,J DOWNE*BLANKS C SUB 1 J 99 *99 - GT 0 C 99 END C* C J IFGT 0 C MOVEA*BLANKS A31,J C END C* C* ..move 30 or less characters to display array C* C ADD 1 K C MOVEAA31 D30,K C ADD J N C END C* C ENDSR C/EJECT C*************************************************************** C* Set initial row/column C*************************************************************** C* C #SETRC BEGSR C* *************** C* C ROW IFLE 0 C ROW ORGT 24 C Z-ADD5 ROW *Row C END C* C COL IFLE 0 C COL ORGE 80 C Z-ADD43 COL *Column C END C* C* ..set column to binary C* C Z-ADDCOL W03N0 *WorkField C EXSR #CVTTB C MOVELW01 WDCOL C* C ENDSR C/EJECT C*************************************************************** C* Format WTD array C*************************************************************** C* C #FMWTD BEGSR C* *************** C* C* ..set WTD command C* C MOVELX04 WDESC1 *Escape 1 C MOVELX50 WDCFT *ClearFmtTable C MOVELX04 WDESC2 *Escape C MOVELX11 WDWTD *WriteToDisplay C MOVELX00 WDCC1 *ControlChar1 C MOVELX08 WDCC2 *ControlChar2 C* C* ..st WTD "template" C* C MOVELX11 WDSBA *SetBufferAddr C MOVELX21 WDSTA1 *StartAttr1 C MOVELX20 WDENA2 *EndAttr2 C* C Z-ADD7 W 50 *WTD ArrayIndex C Z-ADDROW SVROW 20 *Save StartRow C* C* ..format top border / text lines / bottom border C* C 1 DO 8 N C Z-ADDROW W03N0 *WorkField C EXSR #CVTTB C MOVELW01 WDROW C* C ROW IFLT 24 C ADD 1 ROW C END C* C MOVEL*BLANKS WDCLR C* C* ..put text in "lines" 2 through 7 C* C N IFGE 2 C N ANDLE7 C MOVELX20 WDENA1 *EndAttr1 C N SUB 1 K *ArrayIndex C MOVELD30,K WDTXT *DisplayText C MOVELX21 WDSTA2 *StartAttr2 C END C* C MOVEAWTDLN WTD,W C ADD 39 W *Next WTD posn C END C* C Z-ADDSVROW ROW *Restore Row C* C ENDSR C**************************************************************** C* Convert to binary C* C* Receives - W03N0 N(3,0) C* Returns - W01 A(1) C**************************************************************** C* C #CVTTB BEGSR C* *************** C* C Z-ADDW03N0 W03N0 30 *Define field C* C BITOF'01234567'W01 1 *Clear byte C* C W03N0 IFGE 128 C BITON'0' W01 C SUB 128 W03N0 C END C* C W03N0 IFGE 64 C BITON'1' W01 C SUB 64 W03N0 C END C* C W03N0 IFGE 32 C BITON'2' W01 C SUB 32 W03N0 C END C* C W03N0 IFGE 16 C BITON'3' W01 C SUB 16 W03N0 C END C* C W03N0 IFGE 8 C BITON'4' W01 C SUB 8 W03N0 C END C* C W03N0 IFGE 4 C BITON'5' W01 C SUB 4 W03N0 C END C* C W03N0 IFGE 2 C BITON'6' W01 C SUB 2 W03N0 C END C* C W03N0 IFGE 1 C BITON'7' W01 C END C* C ENDSR ** HTX - field text The first field is short, only 5 characters long. This could be used as a c ode field, an abbreviation, or any type of short field. The second field is longer, 50 characters long. This might be a name or des cription field. If you haven't tried it, try changing row/column values.
5250 Data Stream Programming
Figure 5 CL program WTD2CL
WTD2CL: + PGM OVRDSPF FILE(WTDFMT) TOFILE(WTDFMTX) CRTRPGPGM PGM(*CURLIB/WTD2) SRCFILE(*CURLIB/QRPGSRC) + SRCMBR(WTD2) GENLVL(20) OPTION(*NOSRC) DLTOVR FILE(WTDFMT) CALL PGM(WTD2) ENDPGM
5250 Data Stream Programming
Figure 6 Display file WTDFMT
A R TESTFM A CF03(03 'END') A 1 30' WTD "window" example ' A DSPATR(RI) A 7 5'Field 1:' A FIELD1 5 B 7 15DSPATR(UL) A 7 35'Row:' A ROW 2Y 0B 7 40DSPATR(UL) A 7 45'Col:' A COL 2Y 0B 7 50DSPATR(UL) A 12 5'Field 2:' A FIELD2 50 B 12 15DSPATR(UL) A 17 5'Field 3:' A FIELD3 300 B 17 15DSPATR(UL) A R ENTFM A OVERLAY A 22 3'Press ENTER to continue' A R WTDFM USRDFN
5250 Data Stream Programming
Figure 7 Display file WTDFMTX
A R TESTFM A CF03(03 'END') A 1 30' WTD "window" example ' A DSPATR(RI) A 7 5'Field 1:' A FIELD1 5 B 7 15DSPATR(UL) A 7 35'Row:' A ROW 2Y 0B 7 40DSPATR(UL) A 7 45'Col:' A COL 2Y 0B 7 50DSPATR(UL) A 12 5'Field 2:' A FIELD2 50 B 12 15DSPATR(UL) A 17 5'Field 3:' A FIELD3 300 B 17 15DSPATR(UL) A R ENTFM A OVERLAY A 22 3'Press ENTER to continue' A R WTDFM A WTDFL 323 4 2TEXT('WTD command string')
LATEST COMMENTS
MC Press Online