Believe it or not, back in the early 1980's I wrote the first application
that demonstrated the ability to display popup/overlapping windows on 5250
green screens. Initially I used the cryptic User-Defined Data Stream, but
eventually figured out how to do the same thing using the KEEP, ASSUME and
PUTOVR keywords. The CLRL(*NO) keyword was a big help when it was introduced
a couple years later.
The application I created was named PARTNER and when I ported
it to the AS/400 in 1988 it was named Partner/400. This app
contains a popup calendar, notepad, adding machine (with a scrolling
tape), calculator, popup menu, address book, and more. If you ever used Borland's SideKick product back on PC DOS, PARTNER did just about everything
SideKick did, and worked with 5250 screens.
IBM's first app with any kind of a popup window appeared in the Spell
Checker in IBM Text Management. A list of words (spelling alternatives)
would popup during a spell-check request.
Recently while helping one of my clients upgrade to IBM i v7r1, I ran
across several custom Display files that were used to display text to an
end-user. Each display panel had hard-coded text on it that was inside a
format that used the WINDOW keyword. When a
new message was necessary, they would copy the
existing display file DDS source with the windowed text on it, then bring
it into SDA, modify the text, save and compile it. This worked well for
them but made me realize that tracking all those effectively unnecessary
objects wasn't a good choice.
My own RPG xTools has long included an
AskUser subprocedure that pops
up a window with user-supplied text, asking the end-user a Yes/No
question and receiving a response
from the user. Similar to an Inquiry Message but with a popup window
design.
This client's need, however, was for output-only--no end-user response
(other than Enter) was needed. Something like an INFO break
message but with more text.
Rather than continue their technique, I decided to start using the
IBM-supplied QUILNGTX (Display Popup Message) API. This API displays a
fixed-size window overtop of the current screen and displays
user-supplied text. A "user" in this context means "programmer", of
course. Unlike most IBM i APIs, this one is remarkably easy to use
and has only a couple of bogus parameters. Here's the prototype:
.....D QUILNGTX PR extPgm('QUILNGTX')
D szMsgText 1A OPTIONS(*VARSIZE)
D nMsgLen 10U 0 CONST
D szMsgID 7A CONST
D szMsgFile 20A CONST
D api_error Like(QUSEC)
D OPTIONS(*VARSIZE)
The reason I selected this API is that you can send it a string of
characters and it word-wraps text to the window and even displays the infamous
"More..." at the bottom when there's more text to view. This
allows more text to be sent to the popup window than will fit in the
window--while allowing the end-user to scroll through with the Page Up/Down
keys. Here's a quick
screen shot of what it looks like in action:
Example QUILNGTX API in Action
You can see that I've popped up this window over the PDM Member list
display. Unfortunately, you cannot adjust the height or width of the popup
window and it nearly covers the entire screen, so its use may be limited in
some applications.
Note the F12=Cancel function key is enabled automatically to return to
the caller, while F3 is not. However I have discovered that the ENTER key
also works with this API, returning to the caller just like pressing F12.
QUILNGTX API Parameters
This API has 5 required parameters. That is all parameters are
required, but only the first two are useful in most situations. It would
be nice if the first two were required and the rest were optional
Parm 1: Text to display. This parameter may contain (and a kid
you not) up to 15728640 characters of text that are displayed in the
window. I'm speculating that the subfile limit of 16MB has some
structural overhead and that 15728640 figure is 16MB minus that
overhead. So you can display as much text as you need. Note that you do
have to be on V7r1 to start using 16MB field sizes otherwise you may
have to use C APIs to prepare a variable of that size.
Parm 2: Text Length. Specify the length of the text being
passed on parameter 1. Like all APIs, the length parameter is a 4-byte
integer. in RPG IV you declare a 4-byte integers as a 10i0 variable.
Parm 3: Message ID. If you would like a "box title" to appear,
you may specify a CPF message ID (or user message ID) that contains the
title text. The title is centered in the window. The message ID must
exist in the message file specified on the 4th parameter.
Parm 4: Message File. The name of the message file and its
library where the message ID (parameter 3) exists. This is a standard 20
position object - library parameter.
Parm 5: API Error. The standard exception/error API used by
many APIs until IBM decided to change it in some APIs but not others.
The structure is QUSEC in QRPGLESRC in the QSYSINC library.
Practical Example
To illustrate this API I've written a program that loads a text file
stored on the IFS into a variable and then displays that text. This
allows me to use a PC-style editor to edit the text, or the editor built
into the IFS interfaces. The example loads the entire file and sends the
text from that file to the API. Here's the example code:
.....H DFTACTGRP(*NO)
/include qsysinc/qrpglesrc,qusec
/include qsysinc/qrpglesrc,ifs
D QUILNGTX PR ExtPgm('QUILNGTX')
D szMsgText 65535A CONST OPTIONS(*VARSIZE)
D nMsgLen 10U 0 CONST
D szMsgID 7A CONST
D szMsgFile 20A CONST
D api_error Like(QUSEC)
D OPTIONS(*VARSIZE)
// Max Len is 15728640
D len S 10I 0
D msg S 4096A
D LF S 1A Inz(X'0D')
D CR S 1A Inz(X'25')
D apiError DS LikeDS(QUSEC) Inz
C move *ON *INLR
/free
len = loadDailyMsg( msg : %size(msg) );
msg = %ScanRpl( LF : ' ' : msg);
msg = %ScanRpl( CR : ' ' : msg);
quilngtx(msg : len :' ' : ' ' : apiError);
/end-free
P LoadDailyMsg B Export
D LoadDailyMsg PI 20I 0
D szDailyMsg 4096A
D length 10I 0 Const
D nBytes S 20I 0
D nFlags S 10I 0
D ifsFile S 640A Varying
D hFile S 10I 0
/free
ifsFile = '/home/daily.txt';
nFlags = %BITOR(O_RDONLY : O_TEXTDATA);
hFile = open(ifsFile : nFlags );
if hFile < 0;
return 0;
endif;
nBytes = read(hFile : %addr(szDailyMsg) : Length);
if (nBytes <= 0);
return 0;
endif;
callp close(hFile);
return nBytes;
/end-free
P LoadDailyMsg E
The subprocedure LoadDailyMsg loads the content of the IFS file named
DAILY.TXT into a return variable. That text data is the filtered for
ASCII-style CR/LF characters as these characters display on 5250 as little
green boxes. So I xlate them using the %SCANRPL built-in function (see the
highlighted lines in the above example).
To create the DAILY.TXT file I simply started Windows NOTEPAD
application, typed in the text that appears in the example window above,
and saved it. Then I FTP'd it up to the host, into the /HOME directory.
The LoadDailyMsg subprocedure opens that file as an ASCII text file, as
read-only. Then it reads the entire file in one input/read operation.
The number of bytes read is returned by the READ API and is used by the
QUILNGTX API as the length of the text being displayed.
Note the CALLP near the bottom of the LoadDailyMsg subprocedure; this
is required when using the IBM-supplied prototypes. The READ, WRITE,
OPEN, CLOSE IFS APIs have the same names as native RPG IV operation
codes. This creates ambiguity that the compiler can not sort out. To
help the compiler, the context in which these APIs are used must be
cleared up. So the implied EVAL operation on the OPEN and READ APIs and
the CALLP on the CLOSE put these APIs in context. I prefer to
change the prototype names, myself, as using IFSOPEN, IFSCLOSE, IFSREAD,
and IFSWRITE is more clear to me than using APIs whose name match RPG IV
operation codes. But IBM provided these API prototypes so we're stuck
with them unless you copy them and alter their names.
Calling the API in RPG IV requires the use of the prototype feature
earlier in this article. To call this API from within ILE CL, you have to
declare the length variable and also determine the length of the text being
displayed. Sadly, CL still does not have any useful string handling
features, such as %LEN or %CHAR so you have to force it. Here's a simply
call to QUILNGTX from within CL:
TESTPOPUP: PGM
DCL VAR(&MSGTEXT) TYPE(*CHAR) LEN(64)
DCL VAR(&LEN) TYPE(*INT) LEN(4)
DCL VAR(&QUSEC) TYPE(*CHAR) LEN(16) +
VALUE(X'00000000')
MONMSG MSGID(CPF0000)
CHGVAR VAR(&MSGTEXT) VALUE('The batch has been +
sent. Press Enter to continue.')
DOFOR VAR(&LEN) FROM(64) TO(1) BY(-1)
if (%sst(&msgtext &len 1) *NE ' ') LEAVE
ENDDO
CALL PGM(QUILNGTX) PARM(&MSGTEXT &LEN ' ' ' ' &QUSEC)
ENDPGM: ENDPGM
In this example, I use the relatively new DOFOR CL command to loop
backwards through the MSGTEXT variable until I find a non-blank. The LEAVE
command causes the DOFOR loop to end when a non-blank character is detected.
Effectively this means that when the DOFOR loop ends, the LEN variable will
contain the length of the text in the MSGTEXT field. (You're welcome!)
The call to the QUILNGTX API passes only the MSGTEXT and LEN
values. The message ID, message file and QUSEC parameters are specified
with place-holders as they are not used by this example. The first 8
bytes of QUSEC are set to hex-zeros so that the API knows that no
exception/error messages should be returned to the QUSEC variable.
The QUILNGTX API isn't the coolest API around, but it does solve a
problem--dynamically displayed text without the need to design, code,
compile and stub-out displays files just to inform the end-user with an
interface beyond that of a simply SNDPGMMSG command.
Call Me
Bob Cozzi has been providing the solutions to midrange problems,
in the form or articles and books since 1983. He is available
for consulting/contract development or on-site RPG IV, SQL, and CGI/Web training.
Currently many shops are contracting with Cozzi for 1
to 3 days of Q&A and consulting with their RPG staff. Your staff gets to ask real-world
questions that apply to their unique development situations. To contact Cozzi,
send an email to: bob at rpgworld.com
Bob also accepts your questions for use in future RPG Report
articles or content for midrangeNews.com. Topics of interest include: RPG IV,
Web development with RPG IV, APIs, C/C++ or anything else IBM i development related (except subfiles,
data areas and RPGII/III because Bob doesn't care about that stuff) write
your questions
using the COMMENTS link at the bottom of any RPG Report article on the midrangeNews.com website.
You can subscribe to RPG Report (we call it "follow") by
visiting the RPG Report
page
on midrangeNews.com and then click the FOLLOW link in the table of contents
for that
page. To unsubscribe, simply click that same link. You must be
signed up and signed
in to midrangeNews.com to start following RPG Report.
Follow Bob Cozzi on Twitter