Application Development


Chapter 25. Moving Data Between Screens

Panther lets you move data between screens by:


Sending and Receiving Data

Panther provides JPL commands and equivalent library functions to transfer data between screens without LDBs. You typically perform send and receive operations as follows:

  1. Write data—JPL variables, string constants, and widget values—to a buffer, or bundle.
  2. Read data from the bundle into receiving widgets.
advantages over LDBs

Send and receive operations have several advantages over LDB usage:

The following sections describe send and receive operations in general terms. For detailed information on relevant JPL and library function calls and options, refer to Programming Guide.

Bundles

JPL's send and receive commands and Panther API functions act on bundles, which provide temporary storage for the data you wish to transfer between screens. You can name bundles for explicit access. Panther maintains up to ten bundles by default, including one that can be unnamed. If you send data without specifying a bundle name, Panther writes the data to an unnamed bundle; this data is available to the next receive request that omits specifying a bundle name.

You can set the number of available bundles with the application property, max_bundles.

Sending Data

JPL's send command and its library function counterparts write screen data to a buffer that is accessible to other screens.

JPL send

JPL's send command initializes a bundle and populates it with one or more data items. You can send widget and array values, a specific range of occurrences, variables, and constants.

For example, the following send command initializes bundle1 and sends three data items to it. The third data argument, credit[1..], specifies all occurrences in the array:

send bundle "bundle1" data credit_acctno, "1000", credit[1..]

Library Function Calls

If you use Panther library functions, you must issue at least three calls in this order:

  1. Create a new bundle with a call to sm_create_bundle.
  2. Create items in the bundle through successive calls to sm_append_bundle_item.
  3. Populate each bundle item with one or more occurrences of data through sm_append_bundle_data. Each call to sm_append_bundle_data appends a single occurrence of data to the specified item.

When you are finished sending data to a bundle, you can optionally call sm_append_bundle_done to optimize memory allocated for a send bundle.

For example, the following function iterates over all screen-resident widgets and sends their data to the bundle myBundle:

include <smdefs.h>

int sendScreenDataToBundle(int numFields)
{
int i, ret;
if (0 != (ret = sm_create_bundle("myBundle")))
return ret;
for (i = 1; i <= numFields; i++)
{
sm_append_bundle_item("myBundle");
sm_append_bundle_data("myBundle",i,sm_fptr(i));
}
sm_append_bundle_done("myBundle");
return 0;
}

Receiving Data

JPL's receive command and its counterpart sm_get_bundle_data read data from a bundle. The receive command reads bundle data directly into the specified widgets; sm_get_bundle_data reads a single occurrence from the specified bundle item into a buffer and returns with a pointer to that buffer.

JPL receive

JPL's receive command specifies the bundle to read and reads its data items into the target widgets. For example, the following receive command reads bundle1 and puts its data into three widgets:

receive bundle "bundle1" data acctno, credit_amt, credit

receive reads data in the same order that it was sent. Because the bundle retains no information about its data sources, the send and receive calls should sequence widgets in the same order to ensure that the receiving widgets get the correct data. Panther does not check whether receive data is valid for the target widgets.

Unless the receive command includes the keep argument, when it returns, Panther destroys the bundle and frees the memory allocated for it. The keep argument keeps the bundle and its data in memory and available for later receive operations.

Library Function Calls

You can use Panther library functions to ascertain a bundle's state and get individual occurrences of data from it. In the next example, sm_get_bundle_item_count and sm_get_bundle_occur_count, respectively, get the number of items in a bundle and the number of occurrences in each item. This example also gets the data from each specified item occurrence through successive calls to sm_get_bundle_data.

include <smdefs.h>
/*get the bundle item count and pass it along*/
getNumBundleItems(void)
{
if !(is_bundle("myBundle"))
return -1
   getNumOccurs(sm_get_bundle_item_count("myBundle"));
sm_free_bundle("myBundle");
return 0;
}
/*get the occurrence count for each bundle item
*and put occurrence data into screen widgets
*/
void getNumOccurs(int numItems)
{
int itemCt, oCt, item[numItems];

for (itemCt = 1; itemCt <= numItems; itemCt++)
{
item[itemCt] =
sm_get_bundle_occur_count("myBundle", itemCt);
for (oCt = 1; oCt <= item[itemCt]; oCt++)

/*get data from, each item occurrence, put it into
*corresponding widget occurrence
*/
         sm_o_putfield
(itemCt, /*widget number */
oCt, /*occurrence offset*/
         sm_get_bundle_data("myBundle", itemCt, oCt));
}

When you finish reading bundle data, destroy the bundle and free its memory by calling sm_free_bundle.

Panther also provides these library functions:


Using Global Variables

Use the JPL global command to create variables which can be accessed from anywhere in your application. If desired, you can specify the number of occurrences or an initial value.

The following statement taken from a screen's unnamed procedure creates two global variables: one for the user name and one for an array of divisions.

global username, divisions[3] = {"Sales", "HR", "DP"}

In Web applications, there are additional types of global variables. For more information, refer to Chapter 7, "JPL Globals in Web Applications,"in the Web Development Guide.


Accessing Values on Other Screens

If your application has more than one open screen, you can access a value in another screen using the following syntax:

variableName@
screenName

For example, the following screen entry procedure writes the value of the user name from a screen named main.scr to the current screen:

proc screen_entry 
call sm_n_putfield(username, username@main.scr)
return

Note: In Web applications, this syntax is not available.


Using Local Data Blocks

Panther screens can be used as vehicles for initializing and saving values on other screens. A screen that performs this background role is called a local data block, or LDB. When a screen serves as an LDB, Panther uses its widgets, or LDB entries, to transfer data to and from corresponding widgets on the current screen. By using LDBs, applications can transfer data between screens automatically.

Panther matches LDB entries and screen widgets by name. Only named widgets and LDB entries take part in LDB processing, or write-through. One or more screens can be loaded into memory as LDBs and activated. When Panther enters or exits a screen, it checks whether any LDBs are active. If one or more LDBs are active, Panther performs LDB write-through as follows:

If data is transferred between arrays, Panther allocates for the target array the number of occurrences required to accommodate the incoming data, up to the array's maximum number of occurrences.

Selection Groups

Panther regards the selections that the user make in a selection group—radio buttons, toggle buttons, check boxes, and list boxes—as the value of that group. You can use LDBs to propagate that value—that is, repeat the selections—from one screen to another. To ensure consistent results, make sure that the screen selection groups and their corresponding LDB entry have the same number of widgets arranged in the same order, and have the same contents.

Restrictions

In general, you should regard LDBs as passive recipients of data. Although an LDB is created and edited as a screen, at runtime, Panther does not perform most of the processing that is otherwise associated with screens. Screen entry and exit functions are not executed; and no validation or formatting is performed on entry data. For example, no updates occur for an LDB entry that is formatted as a date/time widget with system_update set to PV_YES.

Invalid Targets

Panther does not move values between an LDB and the screen when an error window opens or closes, because these windows do not allow data entry. LDB write-through is invalid for any widget type that is read-only, such as static labels, lines, and boxes.

Data Overflow

LDB entries and their corresponding widgets should have the same data length and number of occurrences. Otherwise, data might be lost for one of these reasons:

Interaction with Screen Modules

LDB write-through occurs after execution of the screen entry function and the screen module's unnamed procedure. Avoid using either venue to write values directly to widgets if the LDB also writes to those widgets. However, you can circumvent this restriction as follows:

  1. Write a procedure or function that populates the widgets with the desired values.
  2. Attach this procedure or function to an unused logical key—for example, APP22=^myproc.
  3. In the screen entry procedure, push this key onto the input queue with the built-in function jm_keys. For example:
    call jm_keys APP22

After the LDB writes its values to the screen, Panther's screen manager pops all data off the input queue. Given the previous example, when Panther gets APP22, it calls myproc and executes its contents.

Loading and Activating LDBs

Multiple LDBs can be loaded into memory; of these, one or more can be active at any time. You can activate an LDB only if it is already loaded; only active LDBs are open to read and write operations. If several LDBs are active and have entries with the same name, Panther uses the entry on the most recently loaded LDB. Use

sm_ldb_get_active to determine which active LDB is most recently loaded and therefore has precedence.

LDB Handles

Panther assigns a unique integer handle to each loaded LDB. Most runtime functions that access loaded LDBs have variants that let you specify the LDB by its handle or by its name. In this chapter, references to functions use name variants only.

loading multiple instances of an LDB

You can load multiple instances of the same LDB. For example, you might do this to prevent data from multiple invocations of the same screen from overwriting each other. Because Panther assigns a unique handle to each loaded LDB, you can reference these LDBs either collectively by their common name, or individually by their separate handles.

using displayed screens as LDBs

A displayed screen can act as an LDB, but only if it is loaded and activated as such. Note that displayed LDB screens offer numerous opportunities for changing LDB data before it reaches its destination—for example, through user input or widget- and screen-level processing. If you display LDB screens, be careful to safeguard this transitional data.

Default Activation

At application startup, Panther tries to load and activate LDBs as follows:

  1. Looks for the configuration variable SMLDBLIBNAME and opens all screens in the specified libraries as LDBs.
  2. Looks for the configuration variable SMLDBNAME. For example:
    SMLDBNAME = screen1.scr | screen2.scr | screen3.scr
  3. Looks for the library ldb.lib and the screens stored in it.

Runtime Loading and Activation

Panther provides several functions for loading and activating, and deactivating and unloading LDBs at runtime:

sm_ldb_load loads an LDB into memory and returns its integer handle.

sm_ldb_state_set lets you activate a loaded LDB and make it available for LDB write-through. Use this function also to deactivate an LDB; the LDB remains loaded but inaccessible to LDB write-through.

sm_ldb_unload removes an LDB from memory, whether active or not.

Read-only LDBs

You can change the state of an LDB from read/write to read-only through sm_ldb_state_set. Screens can read from this LDB on screen entry but cannot modify it on exit; consequently, a read-only LDB cannot be used to transfer values from one screen to another. You can use read-only LDBs to maintain constant values for initializing widget data.

push and pop LDBs

Panther has an LDB save stack for push and pop operations. You can remove all loaded LDBs from memory and push them onto the LDB stack with sm_ldb_push. Each push operation creates a new entry in the stack, which lists the LDB names and their status—whether active or not. Panther maintains stack entries in first-in/last-out order. The number of lists you can save depends on the amount of memory available on your system.

To restore the last-pushed list of LDBs to memory, call sm_ldb_pop. This function removes all loaded LDBs from memory. It then restores to memory the LDBs in the LDB save stack's topmost—that is, most recently pushed—list. If any LDBs were active at the time they were unloaded, sm_ldb_pop restores them to active status.

Getting Information on LDBs

Panther provides several functions that let you get information about loaded and active LDBs and manipulate their values:

widget references and LDB entries

Library functions and JPL procedures that reference widgets by name—for example, sm_n_getfield and sm_n_putfield—seek them first on the screen, then in the LDB. However, on two occasions, Panther reverses the search order: on screen entry or exit, Panther reverses the search order and first looks in the LDB for the requested data. LDB data is written to the screen after screen entry and written back to the LDB before screen exit. Reversing the search order ensures retrieval of the latest data. If the LDB does not contain the requested entry, Panther looks for a corresponding widget on the screen.

You can directly specify LDB entries through sm_ldb_getfield and sm_ldb_putfield and their respective variants. These functions require you to specify an entry's LDB screen by its name or handle. In JPL, you can reference LDB entries as follows:

@ldb( ldb-screen)!ldb-entry