I have written about subfiles, and even a screen with two subfiles on it. When I was asked about a screen with two subfiles, side-by-side, I was intrigued how to do it.
The first problem to overcome is that if I code two subfiles, one to occupy the left side of the screen and the other the right, whichever subfile's control record I write last will overlay the other.
After using Google I could find reference to an example of using DDS windows to contain the subfiles, but the links to the article only returned a "page not found". With that hint I created the following.
Let me start by saying that this display file is a "stripped down" version. I have removed a lot of the things I put in display files so not to clutter this example. Let me start with the file level keywords and a record format I have called HEADER.
01 A DSPSIZ(24 80 *DS3) 02 A PRINT 03 A ERRSFL 04 A INDARA 05 A CA03(03 'F3=Exit') 06 A R HEADER 07 A OVERLAY 08 A 1 31'R P G P G M . C O M' 09 A 2 26'Example screen with + 2 subfiles' 10 A 3 3'*** SUBFILE 1 ***' 11 A 3 40'|' 12 A 3 42'*** SUBFILE 2 ***' |
Line 3: The system's error subfile is so much easier to use than coding your own message subfile.
Line 4: I always use the indicator area,INDARA, in display files, and the Indicator Data Structure in my RPG, so I don't have to use numbered indicators to control my display file in the RPG.
The first subfile and its control record format.
13 A R SFL01 SFL 14 A RRN1 3S 0H 15 A SFL1TEXT 30 O 1 1 16 A R CTL01 SFLCTL(SFL01) 17 A SFLSIZ(0160) 18 A SFLPAG(0016) 19 A OVERLAY 20 A WINDOW(4 1 16 30 + 21 A *NOMSGLIN *NORSTCSR) 22 A WDWBORDER((*CHAR + ' ')) 23 A SFLCSRRRN(&CSRRRN1) 24 A SFLMODE(&MODE) 25 A RTNCSRLOC(&RECORD &FIELD) 26 A 31 SFLDSP 27 A 30 SFLDSPCTL 28 A N30 SFLDLT 29 A 30 SFLEND 30 A CSRRRN1 5S 0H 31 A MODE 1A H 32 A RECORD 10A H 33 A FIELD 10A H 34 A POSRRN1 3S 0H SFLRCDNBR(CURSOR) |
There is nothing extraordinary in the subfile record format.
Lines 17 and 18: This is a "load all" subfile of 160 records, 16 will display on the screen at a time.
Line 20 and 21: This is the section of the code that defines the window. It starts on the fourth line in position 1 and ends at line 16 in position 30. The *NOMSGLIN means that the window will not contain a message line, the display file's default message line will be used. The *NORSTCSR is essential, without it the cursor is restricted to the area of this window. This keyword means that there is no restriction on where the cursor may be placed.
Line 22: When creating and testing this display file I had this line commented out so I could see the borders of the window. This line replaces the window border with blanks, therefore, when display file is executed there is no border.
Lines 23 and 30: I need to know which subfile record the user is on when they press the Enter key. The subfile record number is retrieved into a field, CSRRRN1, that has to be a five long signed numeric field.
Lines 24 and 31: For the SFLCSRRRN to work the subfile must contain this keyword. I am not doing anything with it, therefore, I am not going to bother to describe what is used for.
Line 25 and 32 – 33: This keyword returns the record format and field the cursor is in when a valid key is pressed. In this program I am only concerned with the record format name, RECORD.
Lines 26 – 29: These are the indicators that are used to condition the display of the subfile and control record formats.
Line 34: I will be using this field to keep the position of the subfile, otherwise when I move to the other subfile this one will be displayed starting at the first record.
The second subfile and its control record formats are identical to the first, just with some changes to the names of some of the fields.
35 A R SFL02 SFL 36 A RRN2 3S 0H 37 A SFL2TEXT 30 O 1 1 38 A R CTL02 SFLCTL(SFL02) 39 A SFLSIZ(0160) 40 A SFLPAG(0016) 41 A OVERLAY 42 A WINDOW(4 40 16 30 + 43 A *NOMSGLIN *NORSTCSR) 44 A WDWBORDER((*CHAR + ' ')) 45 A SFLCSRRRN(&CSRRRN2) 46 A SFLMODE(&MODE) 47 A RTNCSRLOC(&RECORD &FIELD) 48 A 33 SFLDSP 49 A 32 SFLDSPCTL 50 A N32 SFLDLT 51 A 32 SFLEND 52 A CSRRRN2 R H REFFLD(CSRRRN1 *SRC) 53 A MODE R H REFFLD(MODE *SRC) 54 A RECORD R H REFFLD(RECORD *SRC) 55 A FIELD R H REFFLD(FIELD *SRC) 56 A POSRRN2 R H REFFLD(POSRRN1 *SRC) 57 A SFLRCDNBR(CURSOR) |
Lines 52 – 56: I love the relational database IBM i. Here I am defining the fields in the second control format to be identical to those in the first.
Right at the bottom is the FOOTER record format. I am showing it here so you know there is no "magic" hidden within it.
58 A R FOOTER 59 A 23 3'F3=Exit' |
The RPG is simple as most of the work is performed in the display file. It has just two procedures, the Main and one to load the subfiles. Let me start with the program's definitions.
01 **free 02 ctl-opt main(Main) 03 option(*nodebugio:*srcstmt) 04 dftactgrp(*no) ; 05 dcl-f TESTDSPF workstn indds(Dspf) 06 sfile(SFL01:RRN1) 07 sfile(SFL02:RRN2) 08 usropn ; 09 dcl-ds Dspf qualified ; 10 Exit ind pos(3) ; 11 Sfl1DspCtl ind pos(30) ; 12 Sfl1Dsp ind pos(31) ; 13 Sfl2DspCtl ind pos(32) ; 14 Sfl2Dsp ind pos(33) ; 15 end-ds ; |
Line 1: I am only going to write this program in totally free RPG.
Lines 2 – 4: The program's control options. As the program is not using the RPG cycle I need to give the name of the Main procedure on line 2, and as no parameters are being passed to this program I do not need a procedure porotype. One line 3 I give my favorite options. And as I have a procedures in the program I need to state that this program will not run in the default activation group, line 4.
Lines 5 – 8: The definition for the display file starts with the Indicator data structure, to correspond with the display's indicator area, line 5. The next two lines define my subfiles' relative record fields. And I will control the opening and closing of the file, line 8.
Lines 9 – 15: The indicator data structure, is qualified on line 9. It contains names for the indicators used in the display file.
The Main procedure is surprisingly simple.
16 dcl-proc Main ; 17 open TESTDSPF ; 18 LoadSubfiles() ; 19 dow (1 = 1) ; 20 write FOOTER ; 21 write HEADER ; 22 if ((RECORD = ' ') or (RECORD = 'SFL01')) ; 23 write CTL02 ; 24 exfmt CTL01 ; 25 read CTL02 ; 26 else ; 27 write CTL01 ; 28 exfmt CTL02 ; 29 read CTL01 ; 30 endif ; 31 if (Dspf.Exit) ; 32 leave ; 33 endif ; 34 POSRRN1 = CSRRRN1 ; 35 POSRRN2 = CSRRRN2 ; 36 enddo ; 37 close *all ; 38 end-proc ; |
Line 16: The Main procedure starts.
Line 17: The display file is opened.
Line 18: The subprocedure to load the two subfiles is called.
Line 19: Start of the Do loop that will be repeated until the I press the F3 key to exit the display and program.
Lines 20 and 21: I write the HEADER and FOOTER record formats. There are no input fields within them so I do not have to read or EXFMT them.
Lines 22 – 30: I can only have one record format use the EXFMT operation code. The other record format can be written and then read after the EXFMT. By using the record format name, returned in the field RECORD, I control which record format should be EXFMT. The first time these lines are reached RECORD is blank, and I have decided to treat that the same as if it was positioned on the first subfile, SFL01. I need to write the control record of the subfile where is the cursor is not, EXFMT the control record of the subfile that the cursor is on, and then read the control record of the subfile where the cursor is not.
Lines 31 – 33: If I press the F3 key, which is mapped to the data structure subfield Dspf.Exit I want to leave the Do loop.
Lines 34 – 36: As both the subfiles are load all I want to preserve the position they were in when I move to the other subfile. I use the value returned by the SFLCSRRRN to position the subfile using the SFLRCDNBR(CURSOR) keyword.
Line 36: End of the Do loop.
Line 37: I close all the open files with this one statement.
Line 38: End of the Main procedure. As this type of program does not use the cycle I do not need *INLR or a RETURN operation code.
As the name suggests the LoadSubfile is used to load the two subfiles.
39 dcl-proc LoadSubfiles ; // Load SFL01 40 Dspf.Sfl1DspCtl = *off ; 41 Dspf.Sfl1Dsp = *off ; 42 write CTL01 ; 43 Dspf.Sfl1DspCtl = *on ; 44 Dspf.Sfl1Dsp = *on ; 45 for RRN1 = 1 to 160 ; 46 SFL1TEXT = 'Subfile 1, line ' + %char(RRN1) ; 47 write SFL01 ; 48 endfor ; // Load SFL02 49 Dspf.Sfl2DspCtl = *off ; 50 Dspf.Sfl2Dsp = *off ; 51 write CTL02 ; 52 Dspf.Sfl2DspCtl = *on ; 53 Dspf.Sfl2Dsp = *on ; 54 for RRN2 = 1 to 160 ; 55 SFL2TEXT = 'Subfile 2, line ' + %char(RRN2) ; 56 write SFL02 ; 57 endfor ; 58 POSRRN1 = 1 ; 59 POSRRN2 = 1 ; 60 end-proc ; |
Lines 40 – 44: This is where the first subfile is initialized and then set to display when the control record is written or EXFMT.
Lines 45 – 48: I am using a For loop to load the subfile with 160 records.
The second subfile is initialized and loaded in exactly the same way.
When I was designing and testing I commented out the WDWBORDER in the display so I could make sure the windows did not overlap. I can move the cursor to one subfile and page up and down. When I want to do the same to the other I need to position the cursor onto it, press Enter, before I can page up and down in it.
Click to see larger version |
When I restore the WDWBORDER the finished display file looks like:
Click to see larger version |
This article was written for IBM i 7.3, and should work for some earlier releases too.
If you had asked me a month ago if it was possible to put two subfiles side by side on a screen, I would have told you "no" and that would have been the end of it.
ReplyDeleteBut I’m working on a project now that would REALLY benefit from being able to do it, so I googled hoping to find someone clever enough to have figured it out. I found this article. As soon as I realized we were simulating it with windows, it started to click.
In my case, I didn’t need side-by-side subfiles. Instead, I needed a dual subfile on the left and some non-subfile content on the right side. Because of the dual subfile screen I had already written, it was easier to just adjust the subfiles narrower leaving the top subfile control and the footer to go the whole length of the screen. That opened up screen space for a window (not subfile) to drop into the right side of the screen. I put a left border character of a vertical bar in the window definition to make the screen look nicer. Works like a charm. Now I wish my users realized how cool it is!
Thanks for the great idea!
Thanks for posting Simon.
ReplyDeleteExcelente, muy bueno: claro, sencillo. Gracias Simon.
ReplyDeleteHuh, really interesting. I've got to play with this, assuming I ever get some free time at work to "play" ~!
ReplyDeleteGreat example Simon. Then "enter" to toggle between subfiles is also very interesting.
ReplyDelete