Release 10.1A: OpenEdge Development:
Progress 4GL Handbook

> ## Examples: Communicating between persistent procedures

> In this section, you extend the test windows to see a couple of different ways persistent procedures can communicate with one another.

> | To add a fill-in field to the main window to locate and manipulate one of the Order windows:

> (You’ll just add code to hide or view it alternately, just to show that you can access it.)

> 1. Open`h-OrderWin.w` and add this statement to the main block to save off the Order Number in the procedure’s `PRIVATE-DATA` attribute:

>     ASSIGN THIS-PROCEDURE:PRIVATE-DATA = STRING(OrderNum)
>            shipDate:BGCOLOR = dateColor(PromiseDate, ShipDate).

> 2. Open `h-CustOrderWin6.w` and add a fill-in to the window. Call it iOrderWin and give it the label Show/Hide Order.

> 3. Define this `LEAVE` trigger for the new fill-in:

>     DO:
>       DEFINE VARIABLE hProc AS HANDLE     NO-UNDO.
>       DEFINE VARIABLE hWin  AS HANDLE     NO-UNDO.
>       hProc = SESSION:FIRST-PROCEDURE.
>       DO WHILE VALID-HANDLE(hProc):
>           IF hProc:FILE-NAME = "OrderWin.w" AND
>              hProc:PRIVATE-DATA = iOrderWin:SCREEN-VALUE THEN
>           DO:
>               hWin = hProc:CURRENT-WINDOW.
>               hWin:HIDDEN = IF hWin:HIDDEN THEN NO ELSE YES.
>               LEAVE.
>           END.
>           hProc = hProc:NEXT-SIBLING.
>       END.
>     END.

> This trigger block walks through the persistent procedure list managed through the `SESSION` handle. When it comes to an instance of `h-OrderWin.w` whose `PRIVATE-DATA` attribute matches the value in the fill-in, it saves off its `CURRENT-WINDOW` attribute, which is the handle of the procedure’s window. If the window is currently hidden it is viewed, and vice versa.

> 4. Save the changes and test them out:

> 1. Run `h-CustOrderWin6.w`.
> 2. Click the Order Detail button for a few different Orders.
> 3. Enter one of the Order numbers into the Save/Hide Order fill-in and tab out of it. The window displaying that Order should disappear, or reappear if it’s already hidden.

> This is a simple example of how to use the `SESSION` procedure list to locate a procedure you’re interested in, and then either manipulate that procedure object or make a request of it in some way.

> The next example goes in the opposite direction. Rather than locating one of the Order windows from the main window, you add code to the Order window procedure to request the handle of another procedure from the main window. It then uses that handle to reposition the other Order window. A new internal procedure in the main window provides an interface the Order window can use to get the handle it needs.

> ##### Using SOURCE-PROCEDURE to identify your caller

> First, you’ll learn another useful built-in function. You’ve seen the `THIS-PROCEDURE` handle function, which holds the handle of the current procedure. Another similar built-in handle function is `SOURCE-PROCEDURE`. Whenever Progress executes a `RUN` statement, the `SOURCE-PROCEDURE` handle in the called procedure returns the procedure handle of the caller.

> | To use `SOURCE-PROCEDURE` to identify your caller:

> 1. To capture the `SOURCE-PROCEDURE` when the Order window first starts up, add this definition to the Definitions section of `h-OrderWin.w`:

>     /* Local Variable Definitions ---                       */
>     DEFINE VARIABLE hSource AS HANDLE     NO-UNDO.

> 2. Add another line to its main block, to save off the value on start-up:

>     ASSIGN THIS-PROCEDURE:PRIVATE-DATA = STRING(OrderNum)
>              hSource = SOURCE-PROCEDURE
>              shipDate:BGCOLOR = dateColor(PromiseDate, ShipDate).

> 3. Add a new fill-in to the window called iOtherOrder, with a Label of Other Order.

> 4. Add a new button called BtnAlign, with a Label of Align Order.

> You can put a rectangle around them to group them if you want, like this:

> 5. Code this `CHOOSE` trigger for BtnAlign:

>     DO:
>       DEFINE VARIABLE hOtherWin AS HANDLE     NO-UNDO.
>       DEFINE VARIABLE hWindow   AS HANDLE     NO-UNDO.
>       RUN fetchOrderWin IN hSource
>           (INPUT iOtherOrder:SCREEN-VALUE,
>            OUTPUT hOtherWin).
>       IF VALID-HANDLE(hOtherWin) THEN
>       DO:
>           ASSIGN hWindow = hOtherWin:CURRENT-WINDOW
>                  hWindow:ROW = THIS-PROCEDURE:CURRENT-WINDOW:ROW + 1
>                  hWindow:COLUMN = THIS-PROCEDURE:CURRENT-WINDOW:COLUMN + 3.
>       END.
>     END.

> This code runs an internal procedure in its source (the main window), which takes an Order number as input and returns as output the handle of the window with that Order number in it. The code then uses the `CURRENT-WINDOW` attribute of that other window and the `CURRENT-WINDOW` attribute of `THIS-PROCEDURE` to align the other window just below and to the right of this one.

> 6. In `h-CustOrderWin6.w`, add another variable to the Definitions section:

>     DEFINE VARIABLE cOrderNumbers AS CHARACTER   NO-UNDO.

> This variable will hold a list of all the Order numbers the procedure opens up windows for.

> 7. Add a statement to the BtnOrder (Order Detail) button to save off the Order number of each window as it’s created, in addition to its procedure handle:

>     DO:
>         DEFINE VARIABLE hOrder AS HANDLE     NO-UNDO.
>         RUN h-OrderWin.w PERSISTENT SET hOrder (BUFFER Order).
>         ASSIGN cOrderHandles = cOrderHandles +
>             (IF cOrderHandles NE "" THEN "," ELSE "") + STRING(hOrder)
>                 cOrderNumbers = cOrderNumbers +
>           (IF cOrderNumbers NE "" THEN "," ELSE "") + STRING(Order.OrderNum).
>     END.

> 8. Define the new `fetchOrderWin` internal procedure in `h-CustOrderWin6.w`:

>     /*---------------------------------------------------------------------
>       Purpose:   Returns the procedure handle to the instance of h-OrderWin.w
>                  that displays the requested Order Number,
>       Notes:
>     ---------------------------------------------------------------------*/
>     DEFINE INPUT  PARAMETER pcOrderNum AS CHARACTER  NO-UNDO.
>     DEFINE OUTPUT PARAMETER phOrderWin AS HANDLE     NO-UNDO.
>     DO iHandle = 1 TO NUM-ENTRIES(cOrderNumbers):
>         IF ENTRY(iHandle, cOrderNumbers) = pcOrderNum THEN
>         DO:
>             phOrderWin = WIDGET-HANDLE(ENTRY(iHandle,cOrderHandles)).
>             RETURN.
>         END.
>     END.
>     END PROCEDURE.

> This code acts as an API call that other procedures like the instances of `h-OrderWin` can run to obtain data from the main window. It looks through the list of Order numbers and, when it finds the one that was requested, it passes back as output the matching procedure handle for that Order.

> | To test out this latest change:

> 1. Run `h-CustOrderWin6.w`.

> 2. Open a couple of Order Detail windows.

> 3. In one of them, enter the Order number displayed in the other, and click the Align Order button (or tab out of the fill-in and press ENTER to do the same thing.) The windows should align like this:

> Why did you use a button with a `CHOOSE` trigger instead of just defining a `LEAVE` trigger for the fill-in? In this case, without the button the fill-in would be the only enabled object in the window, so its `LEAVE` trigger would not fire. You need at least two enabled objects in a window in order to leave one of them. So the button gives you something to tab into and click.

> This latest example shows you in simple terms how you can provide an API in a procedure that other persistent procedures can use to get information from, or to invoke actions in, and a couple of different ways to obtain the handles of other running procedures you’re interested in.

> Now that you have learned the difference between non-persistent and persistent procedures, and how the stack of procedure calls in a typical older Progress application is turned into something more flexible with persistent procedures, it’s time to learn about a programming concept that was very important in those older applications, but which you won’t use often in new applications.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095