The germ for this post's subject came from a two part question I was asked:
- How can I know which field the cursor is in when Enter is pressed?
- How can I position a cursor on some field without using indicators? No indicators are available in that very old screen.
Fortunately there are ways to do this by adding a few keywords and fields to the display file's DDS. In these examples I am going to use one display file with three screens, each showing a way to do what has been asked, and the RPG code needed too (and I am going to cheat on the second part).
Let me start with the file level keywords (header part) for the display file:
01 A DSPSIZ(24 80 *DS3) 02 A PRINT 03 A INDARA 04 A ERRSFL 05 A CA03(03 'F3=Exit') |
If you are regular reader of this site you should recognize my use of the indicator area, line 3, and the error subfile, line 4.
The RPG code for my program starts with these definitions:
01 **free 02 ctl-opt option(*nodebugio:*srcstmt:*nounref) ; 03 dcl-f WA065D workstn indds(Dspf) ; 04 dcl-ds Dspf qualified ; 05 Exit ind pos(3) ; 06 PosToFields char(59) pos(40) ; 07 PosToField1 ind pos(41) ; 08 PosToField2 ind pos(42) ; 09 PosToField3 ind pos(43) ; 10 end-ds ; |
Line 1: As I am on an IBM i running 7.3 I have no excuse for not using totally free RPG.
Line 2: My standard control options I use in all of my programs.
Line 3: This is the definition of the display file I will be using. The INDDS keyword shows I will be using an indicator data area, more on that below.
Lines 4 – 10: Here is the indicator data area, the other end of the INDARA keyword defined in the display file. I am not going to describe what the indicators contained within do until I use them.
And onto the first screen, the one to determine which field the cursor is in when Enter, or any other allowed function key, is pressed.
06 A R SCREEN1 07 A RTNCSRLOC(&Z1REC &Z1FLD) 08 A Z1REC 10A H 09 A Z1FLD 10A H 10 A 2 3'Field 1 .' 11 A FIELD1 1A B 2 14 12 A 3 3'Field 2 .' 13 A FIELD2 1A B 3 14 14 A 4 3'Field 3 .' 15 A FIELD3 1A B 4 14 16 A 6 3'Cursor is in field :' 17 A FIELDNAME 10 O 6 24 18 A 7 4'and record format :' 19 A RECFORMAT 10 O 7 24 |
The parts of this record format to notice are on lines 7 - 9.
Line 7: The Return cursor location keyword, RTNCSRLOC, returns to the program the field and record format names the cursor is in when Enter, or another allowed key, is pressed. The keywords contains two fields, the first to contain the name of the record format and the second for the field name. Notice how both of the field names start with the ampersand character ( & ).
Line 8: This is the field for the record format name. It needs to be 10 characters as that is the maximum length a record format name can be.
Line 9: The field name is placed in this field. This needs to be 10 characters as that is the maximum length a DDS field name should be.
The rest is fairly straight forward. Three entry fields, FIELD1, FIELD2, and FIELD3, and two output fields, FIELDNAME and RECFORMAT.
The RPG for this screen looks like:
20 dow (1 = 1) ; 21 exfmt SCREEN1 ; 22 if (Dspf.Exit) ; 23 leave ; 24 endif ; 25 FIELDNAME = Z1FLD ; 26 RECFORMAT = Z1REC ; 27 enddo ; |
Yes, that's it. Just seven lines of RPG. I don't have to anything to move the display file record format name and field name into the Z1REC and Z1FLD fields, the display does that. I am just moving them to other fields, FIELDNAME and RECFORMAT so they will be displayed on the record format.
When I first call the program the screen looks like this.
Field 1 . Field 2 . Field 3 . Cursor is in field : and record format : |
When I move the cursor to Field 3, and press Enter the name of the field and record format are displayed.
Field 1 .
Field 2 .
Field 3 .
Cursor is in field : FIELD3
and record format : SCREEN1
|
Pretty simple.
Next step is to position the cursor into a field. The record format for this example is:
21 A R SCREEN2 22 A RTNCSRLOC(&Z2REC &Z2FLD) 23 A Z2REC R H REFFLD(Z1REC *SRC) 24 A Z2FLD R H REFFLD(Z1FLD *SRC) 25 A 2 3'Field 1 .' 26 A FIELD1 R B 2 14REFFLD(FIELD1 *SRC) 27 A 41 DSPATR(PC) 28 A 3 3'Field 2 .' 29 A FIELD2 R B 3 14REFFLD(FIELD2 *SRC) 30 A 42 DSPATR(PC) 31 A 4 3'Field 3 .' 32 A FIELD3 R B 4 14REFFLD(FIELD3 *SRC) 33 A 43 DSPATR(PC) 34 A Z2TEXT 50A 6 3 |
I have kept the RTNCSRLOC keyword, line 22, in this format, but changed the names of the fields. I do this as I like to keep this fields unique to the record format. As Z2REC, line 23, and Z2FLD, line 24, are the same as their equivalents in the first record format I have use a Reference Fields keyword, REFFLD, to define them.
The only difference between this and the first record format is that I have used the Display Attribute keyword to position the cursor, DSPATR(PC) on lines 27, 30, and 33, to the field. I have to use an indicator for each of these keywords to differentiate where I want to position the cursor. I know the question said no indicators, but bear with me until I explain the RPG code. I have also added a field, Z2TEXT, so I can display what I am doing.
Here is RPG I created for this screen.
30 dcl-s Screen2Indicators char(59) ; 31 Dspf.PosToFields = Screen2Indicators ; 32 Dspf.PosToField2 = *on ; 33 Z2TEXT = 'Position cursor to FIELD2' ; 34 exfmt SCREEN2 ; 35 Dspf.PosToFields = '0001' ; 36 Z2TEXT = 'Position cursor to FIELD3' ; 37 exfmt SCREEN2 ; 38 Screen2Indicators = Dspf.PosToFields ; |
The questioner said that indicators could not be used as he/she did not have any left to use. Once upon a time a long, long time ago I had to maintain a big old program that had used all of the numbered indicators for the ten screens the program had. It was so long ago this was a RPGIII program that I had to maintain in RPGIII! I did need to move the cursor from field to another using DSPATR(PC), so how to do it when there were no unused indicators? I worked out I could store the indicators from each screen in a variable. I could move the contents of that variable into the indicator array at the start of the subroutine for each screen, and then move the part of the indicator array I used for screen indicators into the variable at the end of the subroutine. No, I am not going to show you how I did that in RPGIII code, I am going to show a modern RPG equivalent.
Line 30: This is the definition of that variable. It is 59 characters long, which corresponds to the indicator data structure subfield PosToFields, line 8.
Line 31: I move the contents of the variable into the indicator data structure subfield. Now the indicators are all set the way were last time this subprocedure or subroutine was executed.
Lines 32 – 34: I want to position the cursor to FIELD2, in this case I am setting on the individual indicator for this field. So now the cursor is in FIELD2:
Field 1 .
Field 2 .
Field 3 .
Position cursor to FIELD2
|
Lines 35 – 37: To position the cursor to FIELD3 I need to turn off the indicator for FIELD2. I could set the FIELD2 indicator off on one line of code, and then set the FIELD3 indicator on on the next line. But why do that when I can do the same on one line, line 35. The cursor is now in FIELD3.
Field 1 .
Field 2 .
Field 3 .
Position cursor to FIELD3
|
Line 38: Before my subprocedure or subroutine ends I want to move those indicator values to my variable I defined on line 30. After this line is executed if I am in debug and I look at the value in this variable I can see that indicator 43, position cursor to FIELD3 is on.
> EVAL Screen2Indicators SCREEN2INDICATORS = ----+----1----+----2----+----3 '000100000000000000000000000000 00000000000000000000000000000' |
I think this gives a good method of how I can still use indicators for positioning the cursor. If I had to absolutely not use indicators then I have to position the cursor by giving the fields position on the screen by line and column. This record format is pretty much the same as the last one.
37 A R SCREEN3 38 A RTNCSRLOC(&Z3REC &Z3FLD) 39 A CSRLOC(Z3LINE Z3COLUMN) 40 A Z3REC R H REFFLD(Z1REC *SRC) 41 A Z3FLD R H REFFLD(Z1FLD *SRC) 42 A Z3LINE 3S 0H 43 A Z3COLUMN 3S 0H 44 A 2 3'Field 1 .' 45 A FIELD1 R B 2 14REFFLD(FIELD1 *SRC) 46 A 3 3'Field 2 .' 47 A FIELD2 R B 3 14REFFLD(FIELD2 *SRC) 48 A 4 3'Field 3 .' 49 A FIELD3 R B 4 14REFFLD(FIELD3 *SRC) 50 A Z3TEXT R 6 3REFFLD(Z2TEXT *SRC) 51 A 23 3'F3=Exit' |
The significant differences are:
Line 39: The Cursor Location keyword, CSRLOC, allows me to position the cursor anywhere on the screen. This keyword had two fields contained within, one for the line number, Z3LINE, and the other for the column, Z3COLUMN.
Lines 40 and 41: These two fields need to be defined as three signed numeric.
40 Z3TEXT = 'Position cursor to FIELD2' ; 41 Z3LINE = 3 ; 42 Z3COLUMN = 14 ; 43 exfmt SCREEN3 ; 44 Z3TEXT = 'Position cursor to FIELD3' ; 45 Z3LINE = 4 ; 46 Z3COLUMN = 14 ; 47 exfmt SCREEN3 ; 48 Z3TEXT = 'Somewhere else' ; 49 Z3LINE = 7 ; 50 Z3COLUMN = 7 ; 51 exfmt SCREEN3 ; |
Lines 40 – 43: To position the cursor to FIELD2 I just need to give its location.
Field 1 .
Field 2 .
Field 3 .
Position cursor to FIELD2
|
Lines 44 – 47: I do the same for FIELD3.
Field 1 .
Field 2 .
Field 3 .
Position cursor to FIELD3
|
But I can position the cursor anywhere, even if there is not a field there.
Field 1 .
Field 2 .
Field 3 .
Somewhere else
|
My concern in using this CSRLOC approach is if I move a field's position on the screen I have to make sure I change its "coordinates" in the RPG too. If I forget to change the RPG the cursor will position where the field is not. This is why I prefer the DSPATR(PC) method. I can move the field anywhere in the record format and not have to change the RPG code.
Some people are bound to ask: "Why not use Program to System fields?"
Alas, one of the few Display Attributes that Program to System fields do not emulate is DSPSATR(PC).
If you are working with a subfile there are times were you want to position a particular subfile record to the top of the display, you can see how to do that in this post about positioning records in a subfile display.
You can learn more about this from the IBM website:
This article was written for IBM i 7.3, and should work for earlier releases too.
Nice topic,
ReplyDeleteIt's a very old technic. The limit is that you must evaluate the exact position of the fields, and that it can change on the screen.
One issue is to use the QUSLFLD API by encapsulating it into a service program wich will retreive the field's line and row.
Finaly, you just need to insert in your program a procedure like this :
DCL-PROC setCursor;
DCL-PI *N;
p_file CHAR(10) CONST;
p_record CHAR(10) CONST;
p_field CHAR(10) CONST;
END-PI;
dsp_rtvPosition(
p_file
:p_record
:p_field
:z3column
:z3line
);
return;
END-PROC;
and you can use it ! :
setCursor('WA065D':'SCREEN3':'FIELD3'); //will position cursor on FIELD3.
I must be missing something in what you mean in your comment.
DeleteIf I use the RTNCSRLOC and the DSPATR(PC) I know what record format and field the cursor is in, and can place it into the field I want just be use of an indicator.
If I move the field to another position on the display I do not have to change any other code as the RTNCSRLOC will still tell me which field the cursor is in, and the DSPATR(PC) will still work too.
What am I missing from your comment?
(I do admit subfiles are the exception to this)
Greetings !
DeleteI know its been a long time since this was posted but, in case anyone is still watching, how would you go about using this QUSLFLD API to get all that information ? The ibm page about QUSLFLD has no example of usage, and no one else on the Internet is talking about this.
Could you, perhaps, show an example ? Maybe here, or in https://pastebin.pl/ ?
Thank you all very much,
I have written about QUSLFLD here.
DeleteIp refer to do this instead.
Sorry my english is very bad :
ReplyDeleteI thought you wanted positionning cursor on a particular field (wich doesn't systematically correspond to current position returned by RTNCSRLOC) without using indicators, for example on a field in error.
In my example a procedure dsp_rtvPosition retrieves the position of a named field and then CRSLOC puts cursor on it.
Hi Denis, Can you post the full code for CRSLOC which I believe is used to position the cursor on to a specific field without using the indicator.
ReplyDeleteHi Anonymous, I'm sorry but the code is too big to post it here. How could we do ?
Delete