Programming Your Own Controls | Migrating to Different Platforms |
This chapter shows you how to use multiple programs and screensets using Dialog System. It covers both Dsrunner and using the call interface to maintain multiple screensets in your application.
When using multiple screensets, you can call Dialog System in two ways:
This is the fastest and simplest way to call Dialog System particularly when you are developing a multi-screenset, multi-module Dialog System application.
Most applications should be able to use Dsrunner. This is the simplest way of calling Dialog System. It enables you to develop a modular Dialog System application with multiple screensets and multiple sub-program modules. You can therefore focus on your business logic and supporting screenset, without needing to worry about the details of calling Dialog System.
The Dsrunner program loads the Dsrunner screenset which is a template for a main window that enables you to launch other screensets. It handles any subprogram and screenset switching as required. This means that you do not have to supply any code to switch between multiple screensets or multiple subprograms.
Dsrunner is a program which runs your application as a subprogram. This means you use Dsrunner to launch a screenset which involves loading the screenset and calling its associated subprogram.You can launch further screensets from that screenset, providing that you have set up your Data Block correctly.
If you want to use multiple screensets and multiple programs, you need to implement a method of program and screenset switching that ensures that the correct screenset and program are loaded when an event occurs. A sample program, called Router, is provided with Dialog System to demonstrate this and is described later in this chapter.
Dsrunner does all the work that Router does and more.
Dsrunner is the main program in an application. The application typically also contains other screensets and sub-programs. The main (Dsrunner) screenset provides the first windows in the application and the Dsrunner program provides functions that load other screensets and subprograms.
Each screenset (optionally) has a subprogram associated with it. The association of a screenset with a subprogram is made by using the same name (except for the file extension) for them both. For example, if a screenset is named dsrnr.gs, its associated subprogram should be named dsrnr.cbl.
When a screenset and its associated subprogram are launched:
This allows any initialization to be performed.
If a screenset executes a RETC instruction, it returns to Dsrunner.
Dsrunner returns control to the screenset.
When the sub-program is called, it is passed four parameters which must appear in the Linkage Section of the program. They must not appear in the Working-Storage Section of the program because they are parameters for a subprogram.
The parameters are:
Provided in screenset.cpb
The screenset-data-block is the Data Block provided for the screenset. The first time the subprogram is called, this Data Block is initialized to LOW-VALUES. Dsrunner does not perform any Data Block version number checking. You must therefore take great care to ensure that the screenset's Data Block and the subprogram's Data Block are kept synchronized.
Provided in dsrunner.cpy.
Provided in dssysinf.cpy. This is exactly as would be returned by Dsgrun.
Provided in ds-cntrl.cpy. It is used by Dsrunner to call Dsgrun. It contains the values that Dsrunner uses to issue the call to Dsgrun, and also contains values returned by Dsgrun on its return, for example, the error-codes, window names, and object names. The ds-control-block is described in detail in the section The Control Block in the chapter Using the Screenset.
Your sub-program can modify the ds-clear-dialog and the ds-procedure fields in ds-control-block to change the way the screenset executes. All other input fields are under the control of Dsrunner and cannot be modified.
Dsrunner can run any screenset without any special actions on your part, providing the screenset-id is defined in the Configuration, Screenset choice on the Options menu. The screenset-id is required so that Dsrunner can switch between screensets.
The Dsrunner screenset supplied with Dialog System, dsrunner.gs is in the DialogSystem\bin subdirectory. This screenset is a template that you can modify. Alternatively you can write your own screenset, but you must make sure that you set up the Data Block correctly and have the required global dialog.
Data Block Header
A Dsrunner screenset must contain the following fields at the start of the Data Block. These fields hold controlling and dispatching information and are:
DSRUNNER-DATA-ITEMS 1 DSRUNNER-SIGNATURE X 8.0 DSRUNNER-FUNCTION-CODE X 4.0 DSRUNNER-RETURN-CODE C 2.0 DSRUNNER-PARAM-NUMERIC C 4.0 DSRUNNER-PARAM-STRING X 256.0
DSRUNNER-SIGNATURE
This is a signature. If you want to launch screensets in turn from a screenset that has already been launched by Dsrunner, you need to specify a signature in your Data Block that indicates it is a Dsrunner screenset.
DSRUNNER-FUNCTION-CODE
This is a function code.
DSRUNNER-RETURN-CODE
This is a return code.
DSRUNNER-PARAM-NUMERIC
and DSRUNNER-PARAM-STRING
These are numeric and string parameters.
If the main screenset does not follow these rules it can still be started. However, no other applications can be launched.
If you want to use a shared memory buffer, the following fields must be inserted immediately after the DSRUNNER-DATA-ITEMS group:
SHARED-MEMORY-BUFFER YOUR-DATA any format or size
You also need to reserve some fields for Dsrunner's use at the start of the Data Block. For this you can import the file dsrunner.imp at the start of the Data Block of the screenset. This import file also contains the key statements required in your global dialog to:
Dsrunner Screenset Requirements
You must ensure that each screenset to be used with Dsrunner:
SET-EXIT-FLAG RETC
This dialog can be in global or local dialog and you can attach it to any suitable event. If you want to tell the subprogram that it is closing down, set your own termination flag and do a RETC before you set the exit flag and do the RETC for Dsrunner.
OTHER-SCREENSET REPEAT-EVENT RETC
This dialog describes the key parts of the global dialog in the supplied Dsrunner screenset:
OTHER-SCREENSET REPEAT-EVENT RETC
This dialog causes an event that occurs for an inactive screenset to be repeated (stacked for the inactive screenset). The RETC causes Dsrunner to switch the active screenset to the correct screenset. This is required if you want to control more than one screenset.
SCREENSET-INITIALIZED MOVE "DSRUNNER" DSRUNNER-SIGNATURE(1)
This dialog sets up the signature indicating that the Data Block of this screenset is set up for Dsrunner. If you do not set up the Data Block, Dsrunner ignores any function codes and a RETC invokes only the subprogram associated with the screenset.
CLOSED-WINDOW EXECUTE-PROCEDURE EXIT-PROGRAM CLOSEDOWN EXECUTE-PROCEDURE EXIT-PROGRAM EXIT-PROGRAM SET-EXIT-FLAG RETC
On a request to close the application, the SET-EXIT-FLAG is issued. This causes Dsrunner to terminate after the RETC. If you want to perform termination processing in your subprogram, set your own termination flag and do a RETC before you set the exit flag and do the RETC to Dsrunner.
OPEN-SCREENSET MOVE "file" DSRUNNER-FUNCTION-CODE(1) MOVE "*.gs" DSRUNNER-PARAM-STRING(1) RETC IFNOT= DSRUNNER-RETURN-CODE(1) 0 OPEN-SCREENSET-ERROR * resulting file name is left in param-string MOVE "lnch" DSRUNNER-FUNCTION-CODE(1) RETC OPEN-SCREENSET-ERROR
This dialog shows how to perform a Dsrunner function. In this example, the file requester is shown and a file name obtained. The function to launch a screenset is then executed.
The Dsrunner program provides certain functions through the first few fields of its Data Block. These include:
For a list of all of the available functions, see the topic Dsrunner Functions in the Help.
Because Dialog System is called by Dsrunner, you set the Dsrunner function code in the Dsrunner Data Block before executing a RETC from the Dsrunner screenset. For example:
LAUNCH-SCREENSET MOVE "lnch" DSRUNNER-FUNCTION-CODE(1) MOVE "screenset-name" DSRUNNER-PARAM-STRING(1) RETC MOVE DSRUNNER-PARAM-NUMERIC(1) SAVED-SS-INSTANCE
In this example, screenset-name
is the name of the
screenset to launch. The function returns the screenset instance, which is
saved as SAVED-SS-INSTANCE
.
Dsrunner is designed to be run from a command line:
runw dsrunner [screenset-name /l /d screenset-name]
Where:
screenset-name is the name of the initial screenset to load. You can enter it in the first positional parameter, or following the /l "load screenset" parameter. You can also specify the /d switch.
/d enables the Screenset Animator immediately, so the Screenset Animator is invoked when the first line of dialog in the first screenset is executed.
Dsrunner is provided in both .obj and .gnt formats to enable you to package your application appropriately for the run-time environment you are using.
Screensets can also be started via Dsrunner in the Net Express IDE:
Any program breakpoints you have will stop execution and load the relevant program into a debug/edit window.
Instead of using a command line, you can call Dsrunner from a program. This enables you to choose your own name for your application and to specify the initial screenset name, without specifying it on the command line. This is useful if your application takes its own command line arguments. It is also useful to perform any application-specific initialization, for example opening a library.
To launch a screenset:
The screenset you select is loaded and the associated program run.
You can select another screenset and run that by repeating the above instructions.
In this section we look at the Dsrunner architecture and consider
what you need to do to be able to use Dsrunner effectively. You can modify
the Dsrunner screenset to meet your own requirements. The supplied
screenset is only an example. You can change the Data Block, but do not
make changes that affect dsrunner-info-block
.
When you launch a screenset, Dsrunner goes through the following procedure:
This program can be any valid COBOL executable in the current directory or $COBDIR (normal COBOL program search rules apply). If the program is not found, a message box is shown with appropriate text.
The following parameters are passed:
Data-Block
Dsrunner-Info-Block
This is provided in dsrunner.cpy and contains:
screenset-id
ds-session-id
screenset-instance-number
Ds-Event-Block
If an error occurs in the call to Dialog System to load the screenset, the subprogram is called with error-codes specified in the Dsrunner information block.
Dsrnr is a sample subprogram supplied with Dialog System. To find out how to launch it and for details of key sections of the sample code, see the chapter Sample Programs.
The Dialog System run-time system can be programmed to enable the use of:
Using these features you can:
When you design your screenset and calling program, you should consider in detail how you control screenset handling, looking at issues such as:
For more information on the basics of screen control using the Dialog System call interface, see the topic The Call Interface in the Help.
You can use multiple screensets by pushing and popping them from the screenset stack. By definition, this is a first in, last out operation. Pushing and popping screensets is useful to:
There are no pre-conditions for pushing a screenset onto the screenset stack, and any screenset, or occurrence of a screenset, can be pushed or popped. Pushed screensets are normally stacked in memory, but if memory is short they will be paged to disk.
The recommended way of developing a large application is to build a separate module for each component and associate each module with its own screenset. Each component is then a separate COBOL program complete with its own user interface.
For example, if you were building an application that has a main data entry component and two utility components, one utility would handle all the printing functions and the other handle the file management functions.
Several screensets can be used in the same application, each being
placed on the screenset stack. This stack is similar in concept to
the call stack that is used whenever you call a program. Calling
screensets in turn is easy, because you simply make the call to Dialog
System with a value of N in ds-control
and with the
new screenset name in ds-set-name
. By default, the old set
is cleared from the screen before a new one is started.
Once you have multiple programs, each with an associated screenset, you need a method to ensure that the correct program and the correct screenset are made active when an event occurs. The sample program that follows, Router, shows you how to structure an application to have multiple programs, each with an associated screenset.
Before you look at the Router program, you need to understand a few terms and concepts.
When several screensets are loaded, we need to distinguish between the active screenset and inactive screensets. Graphical objects displayed by screensets that are presently inactive do not differ in appearance from those displayed by the active screenset. The active screenset is the one that is currently receiving events. Only one screenset can receive events at a particular instant.
When using multiple screensets, you usually specify the active screenset
by calling Dialog System and specifying ds-use-set
in
ds-control
. The screenset specified in ds-set-name
becomes the active screenset. This call is very fast and any overhead
resulting from this swapping is minimal.
If an event occurs for a screenset other than the one that is currently active, a special event, the OTHER-SCREENSET event, occurs in the current screenset. This event simply tells the current screenset that an event has occurred that should be posted to another screenset. Any resulting action depends on the logic in the active screenset.
If the OTHER-SCREENSET event is not found (usually in global dialog), nothing happens. You must specify the OTHER-SCREENSET event so a screenset can detect that an event has occurred for another screenset. Once such an event is detected, you can then make the appropriate screenset active. The most common way is to set a flag indicating that the OTHER-SCREENSET event has occurred and return to the calling program to change screensets.
All events that occur for inactive screensets are returned to the active screenset as OTHER-SCREENSET events.
You can identify the correct screenset by calling Dialog System and
specifying the ds-event-block
. When Dialog System returns to
the calling program, the screenset-id of the screenset that the event was
really for is in ds-event-screenset-id
. This screenset-id is
specified in Configuration, Screenset on the Options menu.
It is not mandatory to specify a screenset-id, but if you do not, you
cannot use multiple screensets because you cannot differentiate between
the screensets on the stack.
When the correct screenset is active, the original event is repeated providing that you have specified the REPEAT-EVENT dialog function within the OTHER-SCREENSET event.
Note: The repeated event is not OTHER-SCREENSET: it is the event that would have occurred if the correct screenset had been active.
A sample application illustrating how to use Router to handle multiple screensets in Dialog System is included in your sample directory. In this example, Programa, the main program, calls subprograms Programb and Programc. Each program has its own screenset.
A fourth program, Router, handles the routing function. Its only purpose is to determine which program (and screenset) needs to be executed next and then call that program.
Figure 11-1 shows the application structure.
Figure 11-1: Structure of Router Application
As well as using multiple screensets, you can use multiple instances of the same screenset. You use multiple instances in a very similar way to using multiple screensets. The real difference is in identifying the screenset. See the section Using Multiple Programs and Screensets before you read this section.
Possible uses of multiple instances of screensets are:
Using multiple instances of the same screenset requires your program to:
When multiple instances are used, a screenset is first started by an "N" or "S" call.
To create a new instance:
ds-push-set
in ds-control
.
ds-screenset-instance
Control Block field.
An instance value is always returned, but you only need to use it when you use multiple instances of a screenset.
The instance value is unique to that particular screenset instance. Your application must keep track of instance values because they are not assigned in any particular order.
You can track the active instance by examining ds-event-screenset-id
and ds-event-screenset-instance-no
within dssysinf.cpy,
which must be copied into your program Working-Storage Section. For more
information on dssysinf.cpy, see the chapter Using Panels V2.
To specify that you want a new instance of a screenset to be loaded:
ds-control
to ds-use-instance-set
when you call Dsgrun. To identify the appropriate instance for a particular event:
ds-event-screenset-instance
. This contains the
appropriate instance value. To call Dialog System with a particular instance value:
ds-event-screenset-instance-no
into ds-instance
.
ds-event-screenset-id
into ds-set-name
.
Notes:
If you are using multiple instances of a screenset, you must maintain multiple copies of the Data Block. You can do this in several ways:
copy "program.cpb"
replacing data-block-a
by data-block-b
.
To push a screenset onto the screenset stack and start a new screenset, call Dsgrun using the following:
move ds-push-set to ds-control call "dsgrun" using ds-control-block, data-block
Where ds-push-set
places the value "S" in
ds-control
. The existing screenset is pushed onto the
screenset stack.
When you pop a screenset off the screenset stack you can use either of the following:
ds-quit-set
This closes the existing screenset and pops the first screenset off the top of the screenset stack.
ds-use-set
This pops the specified screenset off the screenset stack without closing the existing screenset.
For more information see the topic Screenset Animator in the Help.
There are two sample programs which demonstrate the use of the call interface by your program:
You can find key sections of code for these programs in the chapter Sample Programs.
Before we look at the Router program logic, let's take a look at the copyfile that represents a data area shared by Router and all the programs that are called by Router.
The following code shows the copyfile router.cpy, which contains a program name and two flags.
program-name
contains the name of the program to be called
next.
cancel-on-return
is set to TRUE by a program which is to
be cancelled when it returns to Router. If the main program (Programa)
requests cancel, Router sets exit-on-return
, then exits the
main perform loop.
1 01 program-control. 2 3 03 dispatch-flag pic 9(2) comp-5. 4 88 cancel-on-return value 1 false 0. 5 6 03 exit-flag pic 9(2) comp-5. 7 88 exit-on-return value 1 false 0. 8 9 03 program-name pic X(8).
Router starts by calling the main program Programa. When Programa
determines that a sub-program is required to handle a particular function,
it puts the name of the sub-program in program-name
and
exits. Router then calls the program in program-name
.
29 main-section. 30 31* Make sure we don't exit straight away 32 initialize exit-flag 33 34* Start by calling the main program 35 move main-program to program-name 36 37* Call program in program-name until exit is requested 38 perform until exit-on-return 39 40* Remember who we've called 41 move program-name to dispatched-program 42 43* Dispatch program in Program-Name 44 call program-name using 45 if cancel-on-return 46* If last program requested cancel - do cancel 47 set cancel-on-return to false 48 cancel dispatched-program 49 if dispatched-program not = main-program 50* Re-load main program if sub-program cancelled 51 move main-program to program-name 52 else 53* If main program requested cancel, request exit 54 set exit-on-return to true 55 end-if 56 end-if 57 end-perform 58 59 stop run.
If you want to cancel a program, the program must set cancel-on-return
before it returns to Router. If the program specified in main-program
requests cancel, Router exits after cancelling the main program.
This section shows the source for the main program that the Router calls, Programa. The two subprograms Programb and Programc are very similar.
31 procedure division using program-control. 32 33 main-section. 34 if new-instance 35* First time in we push a new screenset onto the stack 36 perform new-set-instance 37 else 38* Once we've initialized use existing screenset 39 perform use-set-instance 40 end-if 41* Call Dialog System as long as we should be active 42 perform until program-name not = this-program-name 43 perform call-ds 44 exit-program.
The main section of Programa checks whether the screenset instance has been created. If it has, the program uses it; if it hasn't, the program creates it.
Once an instance exists, Dialog System is called to display it. Dialog
System is called until the name of the program in program-name
is changed from Programa.
Programa exits (and returns to Router) if program-name
is
not Programa. Router, in turn, calls the program in program-name
.
46 new-set-instance 47 ... 58 ... 59* Push a new screenset onto the stack 60 move ds-push-set to ds-control.
The most important part of new-set-instance
is specifying
ds-push-set
so that when Dialog System is called. it places
the screenset on the stack.
62 use-set-instance. 63* Use existing screenset on the stack 64 move ds-use-set to ds-control 65* This is what we're called 66 move this-program-name to ds-set-name.
When the screenset instance has been created, we can use it by telling Dialog System the screenset name and directing Dialog System to use that screenset from the stack.
67 ... 68 call-ds. 69* Standard initialization and call 70 initialize programa-flags 71 call "dsgrun" using ds-control-block 72 programa-data-block 73 ds-event-block 74 evaluate true 75* When this Screenset flag is set we tell router to exit 76 when programa-terminate-true 77 set exit-on-return to true 78 ... 87 ... 88 when programa-other-set-true 89 move ds-event-screenset-id to program-name 90* If the Programb menu item is selected, this flag is set 91 when programa-program-b-true 92* So we then request Programb to be dispatched 93 move "programb" to program-name 94* If the Programc menu item is selected, this flag is set 95 when programa-program-c-true 96* So we then request Programc to be dispatched 97 move "programc" to program-name 98* Its an event for us 99 when other 100 move "Hello A" to programa-field1 101 end-evaluate.
This code fragment shows a standard call to Dialog System specifying all three parameters, followed by an evaluation. This evaluation directs the overall operation of the program, based on flags set in screenset dialog.
Depending on the flags set in the screenset dialog, Programa exits, switches screenset and program, calls a subprogram, or handles an event for it.
The crucial element of this code is the switching of programs and
screensets when the OTHER-SET-TRUE
flag is set. Dialog
System places the Screenset-ID (set using Configuration, Screenset
on the Options menu) of the screenset for which the event occurred
in ds-event-screenset-id
.
This means that Programa can detect the correct screenset-id and hence program name because this example, conveniently, calls the screenset and its associated program by the same filename. If the screenset and its associated program name differed, you would need to identify the screenset then call the appropriate program.
Programa puts the screenset-id into ds-screenset-name
and
exits; Router will call the identified program and hence load the correct
screenset.
The following example dialog shows screenset dialog needed to handle multiple screensets. The dialog listed here is taken from the Programa screenset, but it is the same for the other screensets.
Dialog System uses the OTHER-SCREENSET event to detect that an event has occurred in a screenset other than the one that is currently loaded. In this example, this event is located in the global dialog table for each screenset:
OTHER-SCREENSET SET-FLAG OTHER-SET(1) REPEAT-EVENT RETC
When such an event is detected, the flag OTHER-SET
is set,
to signal to the program that another screenset is to be used.
Next, the REPEAT-EVENT
function
is executed. REPEAT-EVENT
tells Dialog System to
repeat the last event the next time it goes for input. This occurs when
the next screenset is loaded and control is in the next program. After the
REPEAT-EVENT
, the screenset returns to the calling program
using RETC
.
The calling program, in this case Programa, can then check the screenset
flag. If the flag is set, the program can place the screenset-id in
ds-set-name
(assuming the screenset name and program name
are the same) and exit to Router. Router then calls the appropriate
program and hence loads the correct screenset. Then, because of the
REPEAT-EVENT function issued by the Programa screenset, the event that
caused the OTHER-SCREENSET event is repeated.
Note: The event repeated is not OTHER-SCREENSET; it is the event that would have occurred if the correct screenset had been active.
For more information on these functions, refer to the topic Dialog Statements: Functions in the Help.
The previous sections have described the overall process of switching screensets and the key events that occur. However, if you trace the program flow, all is not quite so simple in practice. There are, in fact, several program switches before control is left in the appropriate program.
Why? The key reason is the change of focus. When the input focus moves from one graphical object to another, two events occur. The object losing focus receives the LOST-FOCUS event, and the object gaining focus receives the GAINED-FOCUS event.
If the object that loses focus is a control object, the LOST-FOCUS event also occurs for the window that contains that object. Similarly, if the object that gains focus is a control object, the GAINED-FOCUS event also occurs for the window that contains the object.
This table shows the sequence of events that actually occurs when there is a simple change of focus from A's window to B's window.
System Event | Active Screenset | Event Screenset | DS Event |
---|---|---|---|
Mouse-button-1-down | A | B | OTHER-SCREENSET |
Lost-focus (window A) | B | A | OTHER-SCREENSET |
Gained-focus (window B) | A | B | OTHER-SCREENSET |
Mouse-button-1-up | B | B | ANY-OTHER-EVENT |
Each screenset traps the OTHER-SCREENSET event and returns to the
calling program from Dialog System. Dialog System is then called with the
correct screenset (identified from ds-event-screenset-id
).
Pressing the mouse button on B's window when A has the focus causes the sequence of events shown in this table.
The system event Mouse-button-1-down on window B causes Dialog System to present an OTHER-SCREENSET event to the active screenset. This gives program A a chance to switch to screenset B.
However, the next system event is window B losing focus. But, as you can see in the table, this event occurs only after the switch to B. This in turn causes an OTHER-SCREENSET event in B.
Another switch of screensets results. B now gains focus when A is active, so another OTHER-SCREENSET event results. This causes B to become active. The mouse button up event is now presented to B as ANY-OTHER-EVENT (because all mouse events are translated to this event).
This sequence of events need not trouble you once you understand the principle of screenset swapping because no matter how many screenset swaps take place, the correct screenset is left active once a steady state is reached.
Note: In this example, the mouse button down event is lost. You can rectify this using the REPEAT-EVENT function within the OTHER-SCREENSET dialog.
First, you must decide whether you need to repeat the event. The purpose of repeating the event is clear. If you need the event, it must be repeated in the correct screenset. However, in many cases, you do not need the event, therefore you do not need to repeat it.
The event sequence caused by clicking buttonB on windowB when focus is currently on buttonA on windowA is the following:
Mouse-Button-1-Down Mouse-Button-1-Up Lost-Focus on buttonA Lost-Focus on windowA Gained-Focus on windowB Gained-Focus on buttonB Button-Clicked on buttonB
This sequence shows that you do not need REPEAT-EVENT in this situation, because the Gained-Focus on windowB loads ScreensetB before the Button-Clicked event causes a BUTTON-SELECTED event.
When a screenset is made active by calling Dialog System and specifying
ds-use-set
in ds-control
, it does not
automatically receive the focus. If the screenset switch is caused by the
user (by selecting a new window or control), the focus moves to the new
screenset because the user's actions cause a GAINED-FOCUS event.
If the switch is caused by the program, you must specify a dialog
procedure name in ds-procedure
before you call Dialog
System. Use a SET-FOCUS dialog function to set the focus on the
appropriate window.
For a detailed description of the call interface see the topic The Call Interface in the Help, which provides information on the Control Block, including the Event Block, the Data Block, use of the Screenset Animator, version checking, and values the calling program returns to Dialog System.
Copyright © 2000 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names
used herein are protected by international law.
Programming Your Own Controls | Migrating to Different Platforms |