Application Development


Chapter 44 . Installed Event Functions

Installed event functions are functions that are written in C and are called at specific events during program execution. Panther recognizes many different stages of program execution as events that can invoke functions—for example, screen entry and exit, widget entry and exit, and client initialization.

All installed functions must be compiled into the application so that Panther can find and execute them at the proper time. Most Panther library functions are already installed for immediate access during the design process.

This chapter shows how to write event functions in C and install them in your application. For information about Panther events and event processing, refer to Chapter 17, "Understanding Application Events."


Installed Function Types

Installed functions can be divided into two general types: those that are called explicitly and those that are called automatically on specific stages of program execution:

Panther can call automatic and demand functions for the same object. For example, on screen entry, the automatic screen function is always called if one is installed. If a screen's Entry Function property also specifies a function, this function is called and executed, too.

Demand Functions

A demand function can be called by name from event properties of any component in a Panther application: groups, screens, menu items, logical keys, and JPL modules. Except for library JPL modules, function calls are stored with the application's screens and can be edited through the screen editor. For example, each widget's properties window lets you specify entry, exit, and validation functions.

Demand functions usually perform tasks that are specific to their callers. For example, you might write an exit function for a widget whose data requires special validation. You then specify this function through the widget's Validation Func property. At runtime, the function is invoked when the user tabs out of the widget.

You can also write demand functions in a JPL module and make the module available to the application through the public command. You can then call that module's procedures—for example, as a widget's entry function, or from a control string; Panther executes the JPL code if no C function of the same name is installed. For more information about JPL, refer to Chapter 19, "Programming in JPL."

Automatic Functions

Automatic functions are called automatically at specific event types. For example, an automatic screen function executes anytime a screen opens and closes. Unlike demand functions, automatic functions are independent of any one widget, screen, or other application component. Automatic functions cannot be written in JPL. However, you can call a JPL procedure from an automatic function through sm_jplcall.

In general, an application can have only one automatic function of each type installed at a time. Thus, there can be only one automatic screen function, one insert toggle function, and so on. Timeout and timer functions are the exception among automatic functions: you can install multiple timeout and timer functions, where each one is called when its own interval expires.


Standard versus Non-standard Arguments

Panther automatically supplies a fixed number of arguments for all installed function types except those that are installed as type PROTO_FUNC (prototyped functions). Arguments that are automatically supplied by Panther are called standard arguments. For example, screen functions get two standard arguments: the screen's name, and a bitmask that tells when and how this function was called. If you use these arguments, you must ensure that function definition parameters correspond in number and type to those supplied by Panther.

You can also write functions whose arguments are explicitly supplied by the application. These functions must be installed as prototyped functions. Panther expects calls to any functions thus installed to supply their own arguments.


Installation

Most functions are typically installed in the source file funclist.c. The coding required to install a function consists of two steps:

  1. Prepare the function for installation by including it in a fnc_data structure. If the function type allows installation of multiple functions, declare an array of fnc_data structures, where each data structure specifies a function.
  2. Install the function with a call to sm_install in the sm_do_uinstalls function.

The following sections describe each of these steps.

Notes: For greater efficiency, prototyped function declarations should be #include'd in funclist.c.

Preparing Functions for Installation

Before you can install a function, you must first include it in a fnc_data structure. The following statements prepare two prototyped functions and one automatic screen function for installation:

struct fnc_data proto_list[] = {
SM_INTFNC ( "mark_flds(i,i)", mark_flds ),
SM_INTFNC ( "report(s,s)", report )
};
struct fnc_data autosc_struct = SM_OLDFNC( 0, auto_sfunc);

Each fnc_data structure is initialized with the following information:

SM_*FNC Macro

Prefix a fnc_data structure with one of several macros that determines the function's return type and whether it dereferences its arguments. Use one of these macros:

Function Name

The first value of a SM_*FNC macro specifies the function's name. Names of prototyped functions must include their argument types. In the previous example, mark_flds takes two integer arguments, while report takes two strings.

If the function type allows installation of only one function, supply 0.

Strings and integers are the only two datatypes that can be passed. If the value is not an integer, pass the value as a string and do the appropriate conversions in your code.

Function Address

The second value of a SM_*FNC macro is the address of the function—that is, its C identifier.

Installing Functions

You install functions through the library function sm_install. For example, given the earlier fnc_data structures, these statements install the functions in proto_list and autosc_struct:

int ct = sizeof ( proto_list ) / sizeof ( struct fnc_data );
sm_install ( PROTO_FUNC, proto_list, &ct ) ;
sm_install ( DFLT_SCREEN_FUNC, &autosc_struct, (int *)0 ) ;

This function takes three arguments:

func_type

Specifies the function type, one of the constants in Table 44-1. In this table, function types are divided into two groups: those that allow installation of multiple functions; and those that allow installation of only one function. Each type is discussed later in this chapter.

Table 44-1 Installed function types

Mupliple function installation

SCREEN_FUNC

Screen Functions

FIELD_FUNC

Field Functions

GRID_FUNC

Grid Functions

GROUP_FUNC

Group Functions

CARD_FUNC

Tab Control Functions

PROTO_FUNC

Prototyped Functions

TIMER_FUNC

Timer Functions

TIMEOUT_FUNC

Timeout Functions

CONTROL_FUNC

Control Functions

TP_INITDATA_FUNC

Client Authentication Functions

TP_INITPOST_FUNC

Client Post-Connection Functions

Single function installation

DFLT_SCREEN_FUNC

Default Screen Function

DFLT_FIELD_FUNC

Default Field Function

DFLT_GROUP_FUNC

Default Group Function

KEYCHG_FUNC

Key Change Function

ERROR_FUNC

Error Function

INSCRSR_FUNC

Insert Toggle Function

EXTERNAL_HELP_FUNC

Help Function

CKDIGIT_FUNC

Check Digit Function

UINIT_FUNC

Initialization Function

URESET_FUNC

Reset Function

RECORD_FUNC

Record Function

PLAY_FUNC

Playback Function

STAT_FUNC

Status Line Function

VPROC_FUNC

Video Processing Function

funcs

The name of the fnc_data structure that includes the functions to install.

num_funcs

The address of a variable that contains the number of functions included in funcs. If funcs is an array of fnc_data structures, get the number of functions declared in the array before calling sm_install. If the function type allows installation of only one function, supply a null integer pointer—(int *)0.

For example, this statement gets the number of functions installed in the fnc_data array proto_list.

int pct = sizeof ( proto_list ) / sizeof ( structfnc_data );


Prototyped Functions

Prototyped functions are functions that get only the number and type of arguments that you specify. Prototyped functions are demand functions—that is, they must be specified by name from a Panther component, such as a widget or screen. Prototyped functions can also be called or referenced in JPL via commands such as call or service_call.

All prototyped functions are installed together in their own function list. When a prototyped function is called, it is supplied the arguments that you specify instead of the standard arguments otherwise supplied by its caller. Thus, if a screen entry event calls a function, and Panther finds this function on the list of prototyped functions, Panther passes the arguments that follow the function's name instead of the two standard arguments otherwise supplied to a screen function. As developer, you must make sure that prototyped function calls supply the correct number and type of arguments.

You can specify prototyped function calls through the screen editor. For example, the screen properties window lets you specify prototyped functions for screen entry and exit. Prototyped functions can also be called in JPL procedures.

Accessing Standard Argument Information

Although prototyped functions that are called by widgets and groups do not get standard arguments, Panther has several library functions that let you get equivalent information about a widget or group.

sm_inquire can return a widget's number, validation state, and occurrence number, and a group's validation state, according to the argument that you supply:

Argument Return Value

SC_AFLDNO

Number of the widget calling a prototyped function. Corresponds to the first standard argument supplied to a widget function.

SC_AFLDMDT

Bit mask that indicates the widget's validation state and why the function was called. Corresponds to fourth standard argument of a widget function.

SC_AFLDOCC

Occurrence number of the widget that called the function. Corresponds to the third standard argument of a widget function.

SC_AGRPMDT

Bit mask that indicates the group's validation state and why the function was called. Corresponds to the second standard argument of a group function.

You can get the second standard argument of a widget function, a pointer to a copy of the widget's contents, through sm_*getfield§ or sm_*fptr.

You can also get the first standard argument of a group function, a pointer to the group name, through sm_getcurno and sm_*ftog at group entry and exit. Access to the group name at group validation is not supported because the group might be undergoing validation as part of screen validation.

Prototyped functions cannot access the standard arguments of a screen. If a function requires this information, you should install it as a demand or automatic screen function.

Installing Prototyped Functions

Prototyped functions are listed with their argument types as members of a fnc_data data structure. The list of argument types is enclosed in parentheses: Panther supports string and integer arguments, specified by s and i, respectively. The following declarations and definitions support the installation of two functions:

struct fnc_data pfuncs[] = {
SM_INTFNC ( "mark_flds(i,i)", mark_flds ),
SM_INTFNC ( "report(s,s)", report )
};

int pfuncs = sizeof ( pfuncs ) /
sizeof ( struct fnc_data ) ;

In this example, marks_flds is prototyped to take two integer arguments and report takes two string arguments.

The macro SM_INTFNC specifies that the function dereferences its arguments and returns an integer value. For string returns, substitute SM_STRFNC; for double precision returns, substitute SM_DBLFNC.

Panther supports any combination of strings and integers from zero to five arguments, and functions with six integer arguments. If a function's arguments do not conform to these requirements—for example, there are more than six, or they include an unsupported data type—you can call it indirectly through a wrapper function.

The following library call to sm_install installs these functions.

sm_install is usually called in sm_do_uinstalls, found in the source module funclist.c:

sm_install( PROTO_FUNC, pfuncs, &pcount ) ;


Screen Functions

You can install an automatic screen function that is called on screen entry and exit. You can also install one or more demand screen functions that can be called explicitly at different stages of program execution. Both automatic and demand screen functions get arguments that describe the screen's current state.

Panther executes the automatic screen function on both entry and exit for that screen. On entry, the automatic screen function is executed before the screen's entry function. If a screen has a JPL module, its unnamed procedure is executed on screen entry before the automatic function. On exit, the screen's exit function is executed before the automatic screen function.

Panther optionally recognizes overlay and reexposure of a screen as exit and entry events, respectively. This depends on how Panther setup variable EXPHIDE_OPTION is set. If the variable is set to ON_EXPHIDE, screen exit and entry functions are invoked on screen overlay and reexposure. Overlay of a screen can occur because another screen opens or is selected; reexposure can occur because an overlying screen closes or is deselected.

Notes: It is not advisable to open a screen from a screen entry function, since such an event yields undefined results.

Screen Function Arguments

All screen functions receive two arguments in this order:

The second parameter can have one or more of the following flags set:

K_ENTRY

The function was called on screen entry.

Equivalent:

if(param2 & K_ENTRY)

K_EXIT

The function was called on screen exit.

Equivalent:

if (param2 & K_EXIT)

K_EXPOSE

The function was called for one of these reasons:

Equivalent:

if (param2 & K_EXPOSE)

K_KEYS

Mask for the bits that indicate which event caused the screen to exit. You should test the intersection of this mask and the second parameter against K_NORMAL or K_OTHER.

K_NORMAL

A "normal" call to sm_close_window caused the screen to close.

Equivalent:

if ((param2 & K_KEYS) == K_NORMAL)

K_OTHER

The screen closed because another form is displayed or because sm_resetcrt is called.

Equivalent:

if ((param2 & K_KEYS) == K_OTHER) 

Screen Function Returns

Screen functions should return 0 if they do not reposition the cursor or change the screen. If a screen function does move the cursor, it should have a non-zero return value, which prevents sm_input from repositioning the cursor.

Installation of an Automatic Screen Function

You can install only one function as the automatic screen function. The following statement, typically found in funclist.c, includes the automatic screen function auto_sfunc in the fnc_data structure autoscr_struct. To see the code for this function, refer to page 44-62.

struct fnc_data autoscr_struct = SM_OLDFNC( 0, auto_sfunc) ;

The following line of code, typically found in the function sm_do_uinstalls in funclist.c, installs auto_sfunc as the default screen function:

sm_install ( DFLT_SCREEN_FUNC, &autoscr_struct, (int *)0 ) ;

Installation of Demand Screen Functions

You can install multiple functions as demand screen functions. The following statements, typically found in funclist.c, include two all-purpose screen entry and exit functions sEntry and sExit in the fnc_data structure sfuncs:

struct fnc_data sfuncs[] =
{
SM_OLDFNC( "sEntry", sEntry ),
SM_OLDFNC( "sExit", sExit ),
};
int scount = sizeof ( sfuncs ) / sizeof ( struct fnc_data ) ;

The following line of code, typically found in the function sm_do_uinstalls in funclist.c, installs the functions in sfuncs as demand screen functions:

sm_install ( SCREEN_FUNC, sfuncs, &scount ) ;

Field Functions

You can install an automatic field function that is called on field entry, exit, and validation. You can also install one or more demand field functions that can be called explicitly at different stages of program execution. Both automatic and demand field functions get arguments that describe the field's current state.

Panther executes the automatic field function on field entry, exit, and validation. You can install an automatic field function that is invoked on any of these events for all fields. You can separately install demand field functions, which individual fields can explicitly invoke for entry, exit, or validation. These functions are installed in the field function list; a field specifies one of these through its properties window as its entry, exit, or validation function.

Automatic field functions can access non-standard information for specific fields through the field's memo text propreties. For an example, see page 44-65.

Execution

Panther executes the automatic field function on all field events. On entry, the automatic field function is executed before the field's entry function. On exit, Panther first calls the field's validation function, then its exit function, and finally the automatic field function. If the field has JPL validation, this module is executed after the validation function.

Entry

Panther can recognize two events as field entry: when the cursor enters a field; and when the screen's current field is reactivated because an overlying window closes, if setup variable EXPHIDE_OPTION is set to ON_EXPHIDE.

Notes: It is not advisable to bring up a dialog box, such as a message dialog, from a field entry function, since opening a screen between a mouse down and a mouse up event yields undefined results.

Exit

Panther can recognize two events as field exit: when the cursor leaves a field; and when a window overlays the field's screen, if setup variable EXPHIDE_OPTION is set to ON_EXPHIDE (the default).

Validation

Validation functions are called under the following conditions:

If a field calls a validation function and belongs to a menu, radio button group, or check box group, this function is called when the field is selected. The validation function of a check box is also called when the field is deselected.

Field Function Arguments

All field functions receive four arguments in this order:

The last parameter can have one or more flags set. The following sections describe these flags:

VALIDED

The field has passed validation and remains unmodified. Note that Panther always calls field functions for validation whether or not the field already passed validation. You can test this flag and the MDT flag to avoid redundant validation. This flag setting can also be accessed and modified through the field's valided property.

Equivalent: if(param4 & VALIDED)

MDT

The field's data changed since the current screen opened. If the screen entry function modifies the field's data when the screen opens, the MDT flag remains unset. However, if the same screen entry function executes because the screen is reexposed—for example, through closure of an overlying window—modification of the field's data sets the MDT flag. This flag setting can also be accessed and modified through the field's mdt property.

Panther never clears this flag once it is set, unless you explicitly clear it by setting the field's mdt property to PV_NO, or clear all fields on the screen by calling sm_cl_all_mdts.

Equivalent: if(param4 & MDT)

K_ENTRY

The field function was called on field entry.

Equivalent: if(param4 & K_ENTRY)

K_EXIT

The field function was called on field exit. Note that if neither K_ENTRY nor K_EXIT are set, the field is undergoing validation.

Equivalent: if(param4 & K_EXIT)

K_EXPOSE

The field function was called because a window overlying this field's screen opened or closed:

K_EXPOSE and K_ENTRY are set: the overlying window closed and the field is exposed.K_EXPOSE and K_EXIT are set: the overlying window opened and the field is hidden.

Equivalent: if(param4 & K_EXPOSE)

K_EXTEND

The field is an extended selection list box.

K_EXTEND_LAST

For extended selection list boxes, the field is the last item in the list box.

K_KEYS

Mask for the flags that tell which keystroke or event caused field entry, exit, or validation. The intersection of this mask and the fourth parameter to the field function should be tested for equality against one of the next six flags.

K_NORMAL

A "normal" key caused the cursor to enter or exit the field in question. For field entry, "normal" keys are NL, TAB, HOME, and EMOH. For field exit, only TAB and NL are considered "normal."

Equivalent: if((param4 & K_KEYS)==K_NORMAL)

K_BACKTAB

The BACKTAB key caused the cursor to enter or exit the field.

Equivalent: if((param4 & K_KEYS)==K_BACKTAB)

K_ARROW

An arrow key caused the cursor to enter or exit the field.

Equivalent: if((param4 & K_KEYS)==K_ARROW)

K_SVAL

The field is being validated as part of screen validation.

Equivalent: if((param4 & K_KEYS)==K_SVAL)

K_USER

The field is being validated directly from the application with sm_fval or sm_validate.

Equivalent: if((param4 & K_KEYS)==K_USER))

K_OTHER

A key other than BACKTAB, an arrow key, or a "normal" key caused the cursor to enter or exit the field. For field entry, "normal" keys are NL, TAB, HOME, and EMOH. For field exit, only TAB and NL are considered "normal."

Equivalent: if((param4 & K_KEYS)==K_OTHER)

K_INSDEL

The INSL or DELL key caused field exit or entry. Use this flag in field exit or entry processing to determine whether the entry or exit event resulted from the user inserting or deleting an occurrence. For example, the following code differentiates between field exit events that are caused by INSL or DELL inserting or deleting an occurrence, and other actions that might cause field exit, such as the user pressing TAB:

if (param4 & K_EXIT)
{
if((param4 & K_KEYS) == K_INSDEL)
// if exit occurred because user inserted or deleted
// occurrence, do nothing
{
return
}
// 'real' exit occurred, so perform exit processing
...
}

Field Function Returns

Field functions called on entry or exit should return 0 if they leave the cursor position unchanged. Field functions called for validation should return 0 if the field contents pass the validation criteria. A non-zero return code in a validation function should indicate that the field does not pass validation.

If the returned value from a field function is 1, the cursor position remains unchanged. Any other non-zero return value repositions the cursor to the field. This repositioning is useful when an entire screen is undergoing validation, because the field that fails validation might not have the cursor in it. It is generally good design practice to use the field validation function to reposition the cursor before you display an error message. This reinforces the link between the error message and the offending field.

Installation of an Automatic Field Function

You can install only one function as the automatic field function. The following statement, usually found in funclist.c, includes the automatic field function auto_ffunc in the fnc_data structure autofld_struct. To see the code for this function, refer to page 44-65.

struct fnc_data autofld_struct = SM_OLDFNC( 0, auto_ffunc ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs auto_ffunc as the default field function:

sm_install ( DFLT_FIELD_FUNC, &autofld_struct, (int *) 0 ) ;

Installation of Demand Widget Functions

You can install multiple functions as demand field functions. The following statements, usually found in funclist.c, include two all-purpose field entry and validation functions fentry and fvalid in the fnc_data structure ffuncs. To see the code for these functions, refer to page 44-69.

struct fnc_data ffuncs[] =
{
SM_OLDFNC( "fentry", fentry ),
SM_OLDFNC( "fvalid", fvalid ),
};
int fcount = sizeof ( ffuncs ) / sizeof ( struct fnc_data ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs the functions in ffuncs as demand field functions:

sm_install ( FIELD_FUNC, ffuncs, &fcount ) ;

Grid Functions

You can install one or more demand grid functions that can be called explicitly at different stages of grid execution:

Panther can recognize two events as grid entry: when the cursor enters a grid; and when the screen's current grid is reactivated because an overlying window closes, if setup variable EXPHIDE_OPTION is set to ON_EXPHIDE.

On grid entry, the grid's entry function is executed first, then the grid's row entry function. The grid row exit and entry functions are repeatedly called each time the cursor exits the current row and enters another one.

Panther can recognize two events as grid exit: when the cursor leaves a grid; and when a window overlays the grid's screen, if setup variable EXPHIDE_OPTION is set to ON_EXPHIDE. On exit, the grid row exit function is called before the grid exit function.

Grid Function Arguments

All grid functions receive three arguments in this order:

The last parameter can have one or more flags set. The following sections describe these flags:

K_ENTRY

The function was called on grid or row entry.

Equivalent: if (param3 & K_ENTRY)

K_EXIT

The function was called on grid or row exit. Note that if neither K_ENTRY nor K_EXIT are set, the grid is undergoing validation.

Equivalent: if (param3 & K_EXIT)

K_EXPOSE

The function was called because a window overlying this grid's screen opened or closed:

K_EXPOSE and K_ENTRY are set: the overlying window closed and the grid is exposed.

K_EXPOSE and K_EXIT are set: the overlying window opened and the grid is hidden.

Equivalent: if(param3 & K_EXPOSE)

K_KEYS

Mask for the flags that tell which keystroke or event caused entry, exit, or validation for the grid or grid row. The intersection of this mask and the third parameter to the function should be tested for equality against one of the next six flags.

K_NORMAL

A "normal" key caused the cursor to enter or exit the grid or grid row in question. For grid or grid row entry, "normal" keys are NL, TAB, HOME, and EMOH. For grid exit, only TAB and NL are considered "normal."

Equivalent: if((param3 & K_KEYS)==K_NORMAL)

K_BACKTAB

The BACKTAB key caused the cursor to enter or exit the grid or grid row.

Equivalent: if((param3 & K_KEYS)==K_BACKTAB)

K_ARROW

An arrow key caused the cursor to enter or exit the grid or grid row.

Equivalent: if((param3 & K_KEYS)==K_ARROW)

K_SVAL

The grid is being validated as part of screen validation.

Equivalent: if((param3 & K_KEYS)==K_SVAL)

K_USER

The grid is being validated directly from the application with sm_fval.

Equivalent: if((param3 & K_KEYS)==K_USER))

K_OTHER

A key other than BACKTAB, an arrow key, or a "normal" key caused the cursor to enter or exit the grid or grid row. For entry, "normal" keys are NL, TAB, HOME, and EMOH. For exit, only TAB and NL are considered "normal."

Equivalent: if((param3 & K_KEYS)==K_OTHER)

K_INSDEL

The INSL or DELL key caused grid row exit or entry. Use this flag in row exit or entry processing to determine whether the entry or exit event resulted from the user inserting or deleting a grid row. For example, the following code differentiates between row exit events that are caused by INSL or DELL inserting or deleting a gird row, and other actions that might cause row exit, such as the user pressing TAB:

if (param4 & K_EXIT) 
{
if((param3 & K_KEYS) == K_INSDEL)
// if exit occurred because user inserted or deleted
// row, do nothing
{
return
}
// 'real' exit occurred, so perform exit processing
...
}

Grid Function Returns

Grid functions return meaningful values only if called as the grid's validation function—0 if successful, non-zero if not.

Installation of Demand Grid Functions

You can install multiple functions as demand grid functions. The following statements, typically found in funclist.c, include two all-purpose grid entry and exit functions gridEntry and gridExit in the fnc_data structure grdfuncs:

struct fnc_data grdfuncs[] = 
{
SM_INTFNC( "gridEntry", gridEntry ),
SM_INTFNC( "gridExit", gridExit ),
};
int gcount = sizeof ( grdfuncs ) / sizeof (struct fnc_data) ;

The following line of code, typically found in the function sm_do_uinstalls in funclist.c, installs the functions in grdfuncs as demand grid functions:

sm_install ( GRID_FUNC, grdfuncs, &gcount ) ;

Tab Control Functions

You can install one or more demand tab card functions that can be called explicitly at different events:

Since the index tab on each card is a field, refer to refer to page 44-14 for information on field functions.

Tab Control Function Arguments

All tab card functions receive two arguments in this order:

The last parameter can have one or more flags set. The following sections describe these flags:

K_ENTRY

The function was called on tab card entry.

Equivalent: if(param2 & K_ENTRY)

K_EXIT

The function was called on tab card exit.

Equivalent: if(param2 & K_EXIT)

K_EXPOSE

The function was called because the screen containing this card opened or was exposed, because the overlying tab card was hidden, or because this tab card is the new topmost card.

Equivalent: if(param2 & K_EXPOSE)

For the card hide event, all three flags are clear.


Group Functions

Panther calls group functions on entry, exit, and validation of groups. You can install an automatic group function is invoked on entry, exit, and validation for all groups. Each group can invoke its own functions for these events through its entry, exit, and validation functions. You can specify these event functions in the group's properties window, accessed through the screen editor.

The automatic group function is executed on all group events. On entry, the automatic group function is executed before the group's entry function. On exit, the group's exit and validation functions is called first, and then the automatic group function. If the group has a JPL group module, this module is executed only after the group functions.

Two events are recognized as group entry: when the cursor enters a group; and when the screen's current group is reactivated because an overlying window closes.

Two events are recognized as group exit: when the cursor leaves a group; and when a window overlays the group's screen.

Group validation functions are called under the following conditions:

Note that if a group contains a widget that has its own validation function, this function is called when the widget is selected. The validation function of a check box is also called when the widget is deselected.

Notes: It is not advisable to bring up a dialog box, such as a message dialog, from a group entry function, since opening a screen between a mouse down and a mouse up event yields undefined results.

Group Function Arguments

All group functions receive two arguments:

The flags that can be set on a group's bitmask are the same as for a single widget. For a description of these flags, refer to page 44-16.

Group functions are called for validation whether or not the group has already been validated. You can test the VALIDED and MDT bits to avoid redundant processing.

Group Function Returns

Group functions called on entry or exit should return 0. Group functions called for validation should return 0 if the group selections pass the validation criteria. A non-zero return code should indicate that the group failed validation. If the return value is 1, the cursor position remains unchanged. Any other non-zero return value repositions the cursor to the group that failed validation.

Installation of an Automatic Group Function

You can install only one function as the automatic group function. The following statement, usually found in funclist.c, includes the automatic group function auto_gfunc in the fnc_data structure autogrp_struct. To see the code for this function, refer to page 44-71.

struct fnc_data autogrp_struct = SM_OLDFNC( 0, auto_gfunc ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs auto_gfunc as the default group function:

sm_install ( DFLT_GROUP_FUNC, &autogrp_struct, (int *) 0 ) ;

Installation of Demand Group Functions

You can install multiple functions as demand group functions. The following statements, usually found in funclist.c, include two all-purpose group entry and exit functions gEntry and gExit in the fnc_data structure gfuncs:

struct fnc_data gfuncs[] =
{
SM_OLDFNC( "gEntry", gEntry ),
SM_OLDFNC( "gExit", gExit ),
};
int gcount = sizeof ( gfuncs ) / sizeof ( struct fnc_data ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs the functions in ffuncs as demand widget functions:

sm_install ( GROUP_FUNC, gfuncs, &gcount ) ;

Client Authentication Functions

With the JetNet/Tuxedo middleware adapters, a client authentication function is used by the middleware's authentication server during client initialization. This function, specified by client_init's DATAFUNC option, provides the data required to authorize the client connection. For more information about client authentication options in JetNet and Tuxedo, refer to page 2-11 in the Programming Guide.

Client Authentication Arguments

A client connection authentication (DATAFUNC) function requires five arguments:

Client Authentication Returns

DATAFUNC functions should return a long containing the non-negative length of the authentication data at the address (first argument). A positive value must be accompanied by a non-zero address in the authentication data address. If the function returns a negative value, or if the address is NULL when a positive value is returned, client_init raises a TP_DATAFUNC_FAILED exception.

Installation

You can install multiple DATAFUNC functions. The following statements, typically found in funclist.c, includes the DATAFUNC function user_datafunc in the fnc_data structure authpre. Examples are given for both a single DATAFUNC and multiple functions.

Install a single DATAFUNC function as follows:

struct fnc_data authpre = 
	SM_STRFNC ("user_datafunc", user_datafunc);

The following line of code, typically found in the function sm_do_uinstalls in funclist.c, installs user_datafunc as the default client authentication connection function:

sm_install ( TP_INITDATA_FUNC, authpre, (int *)0);

Install multiple DATAFUNC functions by defining them in an array of fnc_data structures:

static struct fnc_data authpre[] =  
{
SM_STRFNC ("user_datafunc1", user_datafunc1),
SM_STRFNC ("user_datafunc2", user_datafunc2)
};
static int acount = 
		sizeof(authpre) / sizeof (struct fnc_data);

The following line of code, typically found in the function sm_do_uinstalls in funclist.c, installs the functions in authpre as DATAFUNC functions, to be called depending on the argument supplied by client_init's DATAFUNC option:

sm_install (TP_INITDATA_FUNC, authpre, &acount);

Client Post-Connection Functions

With the JetNet/Tuxedo middleware adapters, client_init can include a POSTFUNC option that specifies a function to be paired with its DATAFUNC function. A POSTFUNC function is always called whether or not the client authentication is successful. For more information about client authentication options in JetNet and Tuxedo, refer to page 2-13 in the Programming Guide.

Client Post-Connection Arguments

All POSTFUNC functions require six arguments:

Client Post-Connection Returns

void (none)

Installation

You can install multiple POSTFUNC functions. The following statements, typically found in funclist.c, includes the POSTFUNC function user_postfunc in the fnc_data structure authpost. Installation instructions are shown for both a single POSTFUNC and multiple functions.

Install a single POSTFUNC function as follows:

Define the function in a fnc_data structure.

struct fnc_data authpost = 
	SM_STRFNC ("user_postfunc", user_postfunc);

The following line of code, typically found in the function sm_do_uinstalls in funclist.c, installs user_postfunc as the default client authentication post-connection function:

sm_install ( TP_INITPOST_FUNC, authpost, (int *)0 );

Install multiple POSTFUNC functions by defining them in an array of fnc_data structures:

static struct fnc_data authpost[] =  
{
SM_STRFNC ("user_postfunc1", user_postfunc1),
SM_STRFNC ("user_postfunc2", user_postfunc2)
};
static int acount = 
		sizeof(authpost) / sizeof (struct fnc_data);

The following line of code, typically found in the function sm_do_uinstalls in funclist.c, installs the functions in authpost as client authentication post-connection functions, to be called depending on the argument to the POSTFUNC option used in a call to the client_init command.

sm_install (TP_INITPOST_FUNC, authpost, &acount);

For further information on client authentication, refer to client_init.


Help Function

A help function installs a driver to invoke an external help facility such as WINHELP from your application. This driver gets a single argument from its caller, which contains a help context identifier. It is the responsibility of the help driver to pass this identifier to the help facility.

Help Function Arguments

The help function gets a single string that contains the help context identifier.

Help Function Returns

PI_ERR_NONE (success) or 
PI_ERR_NO_MORE (failure).

Installation

You can install only one function as a help function. The following statement, usually found in funclist.c, include the help function sm_PiXmDynaHook in the fnc_data structure hlp_struct. To see the code for this function, refer to page 44-73.

struct fnc_data hlp_struct = SM_OLDFNC( 0, sm_PiXmDynaHook );

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs sm_PiXmDynaHook as the default help function:

sm_install ( EXTERNAL_HELP_FUNC, &hlp_struct, (int *) 0 );

Timeout Functions

Panther periodically calls the installed timeout functions while the keyboard input function awaits user input. You can use timeout functions to poll or otherwise manipulate communications resources, or to update the screen display. You can install multiple timeout functions with different time lapse specifications, measured in minutes, seconds, or tenths of seconds. Each timeout function is called when its timeout interval elapses.

Timeout functions are called from the lowest level of keyboard or mouse input. When they are installed, the device driver clock on the terminal input device is set to time out on its character read operation. If Panther does not read any character in the time interval specified by a timeout function, it calls that function before it tries to read another character.

Timeout Function Arguments

Timeout functions get one integer argument that tells why the function was called:

TF_TIMEOUT

No keyboard activity occurred for the amount of time specified by this function's timeout interval.

TF_RESTART

Keyboard input was received during execution of the timeout function.

Timeout Function Returns

A timeout function should return a code that indicates whether Panther should keep calling the timeout function after each lapse of the timeout interval:

TF_KEEP_CALLING

Keep calling the user function each timeout the interval elapses.

TF_STOP_CALLING

Do not call the timeout function again until keyboard input is received.

Installation

You can install multiple timeout functions. The following statements, usually found in funclist.c, installs a single timeout function screen_saver in the fnc_data structure timeout_funcs. To see the code for this function, refer to page 44-78. The first member of this structure specifies units of measurement: TF_TENTHS (tenths of seconds), TF_SECONDS, or TF_MINUTES. The fifth member specifies the timeout interval as a multiple of these units.

struct fnc_data timeout_funcs[] =
{
{ TF_MINUTES, screen_saver, 0, 0, 10, 0 }
};
int tcount = sizeof ( timeout_funcs )
/ sizeof (struct fnc_data ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs the function in timeout_funcs as a timeout function:

sm_install ( TIMEOUT_FUNC, timeout_funcs, &tcount ) ;

Timer Functions

Panther periodically calls installed timer functions while the keyboard input function awaits user input. Timer functions differ from timeout functions in that the interval for timer functions is not reset after user input. You can use timer functions to poll or otherwise manipulate communications resources, or to update the screen display. You can install multiple timer functions with different time lapse specifications, measured in minutes, seconds, or tenths of seconds. Each timer function is called when its interval elapses.

Timer Function Arguments

Timer functions get one integer argument that tells why the function was called:

TF_TIMEOUT

The timer's interval has expired.

TF_RESTART

There was a timer interval expiration for which the function was not called. This could happen, for example, during a long database operation. TF_RESTART calls only occur after TF_TIMEOUT calls. If the timer function returns TF_STOP_CALLING, no more TF_RESTART calls will occur before the next TF_TIMEOUT call.

Timer Function Returns

A timer function should return a code that indicates whether Panther should keep calling the function after each lapse of the timer interval:

TF_KEEP_CALLING

Call the timer function extra times if timer expiration calls were missed.

TF_STOP_CALLING

Do not call the timer function again until the next timer expiration.

Installation

You can install multiple timer functions. The following statements installs a single timer function poll_database in the fnc_data structure timer_funcs. The first member of this structure specifies units of measurement: TF_TENTHS (tenths of seconds), TF_SECONDS, or TF_MINUTES. The fifth member specifies the timer interval as a multiple of these units.

struct fnc_data timer_funcs[] =
{
{ TF_MINUTES, poll_database, 0, 0, 10, 0 }
};
int tcount = sizeof ( timer_funcs )
/ sizeof (struct fnc_data ) ;

The following line of code installs the function in timer_funcs as a timeout function:

sm_install ( TIMER_FUNC, timer_funcs, &tcount ) ;

Key Change Function

A key change function can be called whenever Panther reads a key from the keyboard. You can use key change functions to intercept, process, or translate keystrokes at the logical key level. Key change functions can be useful alternatives to using sm_keyoption.

Panther calls the key change function once for each key that it gets from the keyboard or the playback function.

Note: The key change function ignores any keys placed on the input queue by sm_ungetkey or jm_keys.

Key Change Function Arguments

A key change function gets one integer argument, the Panther logical key that is read from the keyboard or received from a playback function.

Note: The key change function is not called for the following keys: MNBR, ALSYS, and ALT keys.

Key Change Function Returns

A key change function returns the key to be input into the application by sm_getkey. If the key change function returns 0, sm_getkey gets the next key from the keyboard.

Installation

You can install only one key change function. The following statement, usually found in funclist.c, includes key change function keychg in the fnc_data structure keychg_struct. To see the code for this function, refer to page 44-79.

struct fnc_data keychg_struct = SM_OLDFNC( 0, keychg ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs keychg as the key change function:

sm_install ( KEYCHG_FUNC, &keychg_struct, (int *) 0 ) ;

Error Function

Panther calls the installed error function when it issues an error message—invoked either by a Panther error or by a call to one of Panther's error message functions—for example, sm_fquiet_err or sm_ferr_reset. You can use the error function for special error handling—for example, to write all error messages to a log file.

Error Function Arguments

The error function gets three arguments in this order:

Error Function Returns

If the error function returns 0 to its caller, the calling message function continues processing. If this function returns a non-zero value, the calling message function returns immediately.

Installation

You can install only one error function. The following statement, usually found in funclist.c, includes error function myerr in the fnc_data structure err_struct.

struct fnc_data err_struct = SM_OLDFNC( 0, myerr ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs myerr as the error function. To see the code for this function, refer to page 44-81 .

sm_install ( ERROR_FUNC, &err_struct, (int *) 0 ) ;

Insert Toggle Function

An insert toggle function is called when the data entry mode switches between insert and overstrike mode—for example, when the user chooses Insert. You can use this function to display a message that indicates the current mode.

Panther automatically installs an insert toggle function that changes the cursor style when the mode is changed. If an application has its own insert toggle function installed, Panther deinstalls its insert toggle function; the insert toggle function that you install can call Panther's insert toggle function directly.

Arguments

This function gets one integer argument, which specifies the new mode:

Returns

The insert toggle function should return 0.

Installation

You can install only one insert toggle function. The following statement, usually found in funclist.c, includes the insert toggle function inscrsr in the fnc_data structure keychg_struct. To see the code for this function, refer to page 44-81.

struct fnc_data instgl_struct = SM_OLDFNC( 0, inscrsr ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs inscrsr as the insert toggle function:

sm_install ( INSCRSR_FUNC, &instgl_struct, (int *) 0 ) ;

Check Digit Function

A check digit function is called during validation of any widget whose Check Digit property is set. You use a check digit function to perform your own check digit algorithm. If no check digit function is installed, the library function sm_ckdigit is used, whose source is distributed in source form.

Because sm_ckdigit source is available, you can implement your own algorithm by directly modifying this library function and linking it to your application. However, if your linker does not let you override library functions, you must install your own check digit function.

Arguments

The check digit function gets these arguments:

Returns

The check digit function should return 0 if the widget passes check digit validation. If the function returns a non-zero value, the cursor is repositioned to the offending widget and the widget is not marked as validated.

Installation

You can install only one check digit function. The following statement, usually found in funclist.c, includes the check digit function ckdigit in the fnc_data structure ckdgt_struct.

\Qstruct fnc_data ckdgt_struct = SM_OLDFNC( 0, ckdigit ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs ckdgt as the check digit function:

sm_install ( CKDIGIT_FUNC, &ckdgt_struct, (int *) 0 ) ;

Initialization and Reset Functions

The initialization and reset functions are called on display setup and reset, respectively. You can use the initialization function to set the terminal type, and the reset function to handle any cleanup that the application requires on exit.

The initialization function is called from the library function sm_initcrt. It is called before Panther allocates its own memory structures or sets the physical display. Unlike other installed functions, the initialization function should be installed before sm_initcrt is called. Consequently, you cannot place the installation code for this function in the funclist.c function sm_do_uinstalls.

The reset function is called from the library function sm_resetcrt after Panther releases its memory and resets the physical display. Because Panther's abort function sm_cancel calls sm_resetcrt before the application terminates, it calls the reset function at application exit whether the exit is graceful or not.

You might need to set interrupt handlers to ensure that sm_cancel is called at all the necessary hardware and software interrupt signals. You should set these either in the funclist.c function sm_do_uinstalls, or in the function installed as an initialization function.

Arguments

The initialization function is passed a single argument, a 30-byte character buffer that contains a null-terminated string mnemonic for the terminal type in use. This is mainly used for operating systems without an environment. You can use this function to get the terminal type in some system-specific way.

The reset function is passed no arguments.

Returns

Both the initialization and reset functions should return 0.

Installation

You can install only one initialization and one reset function. Initialization functions are called by sm_initcrt and so must be installed in jmain.c before the call to sm_initcrt:

struct fnc_data uninit_struct = SM_OLDFNC( 0, uinit ) ;
sm_install ( UINIT_FUNC, &uinit_struct, (int *) 0 ) ;

The reset function can be installed like other functions in funclist.c. This function is called from sm_resetcrt, and is consequently called even if the application terminates abnormally:

struct fnc_data ureset_struct = SM_OLDFNC( 0, ureset ) ;
sm_install ( URESET_FUNC, &ureset_struct, (int *) 0 ) ;

To see sample initialization and reset functions, refer to page 44-82.


Record and Playback Functions

Panther provides hooks for recording and playing back keystrokes. You can use this facility to create simple macros, or to perform regression testing on a Panther application. Be careful that record and playback functions are not in use simultaneously:

Characters are recorded after the key change function processes them, but are played back before key change translation; consequently, some key change functions might prevent accurate playback of recorded keystrokes. Refer to the description of sm_getkey for more information.

Accurate regression testing might require the playback function to pause and flush the output, in order to simulate a realistic rate of typing, and to call a timeout function.

Arguments

The record function gets a single integer argument, the Panther logical key to record. This key is usually recorded in some fashion for later playback.

The playback function gets no arguments.

Returns

The record function should return 0. The playback function should return the logical key previously recorded.

Installation

You can install only one record and one playback function. The following statements, usually found in funclist.c, include the record and playback functions record and play in the fnc_data structures record_struct and play_struct, respectively. To see the code for these functions, refer to page 44-84.

struct fnc_data record_struct = SM_OLDFNC( 0, record ) ;
struct fnc_data play_struct = SM_OLDFNC( 0, play ) ;

The following lines of code, usually found in the function sm_do_uinstalls in funclist.c, install record and play as the record and playback functions:

sm_install ( RECORD_FUNC, &record_struct, (int *) 0 ) ;
sm_install ( PLAY_FUNC, &play_struct, (int *) 0 ) ;

Control Functions

Control functions are called either through control strings or by the call command in a JPL procedure. Because control functions take only one argument—a pointer to a copy of the control string that invoked it—you can install as control functions those functions whose argument list is especially long or complex—for example, a SQL statement.

All control functions are demand types—that is, they must be explicitly named by one of the aforementioned callers.

Arguments

A control function receives one argument, a pointer to a copy of the control string or call command that invoked it. This string is stripped of its leading caret ^ or call verb. Panther identifies only the first word of the control string as the function name; the rest of the string can be parsed and used as arguments by the function.

Returns

Control functions can return any integer. You can use the return value for conditional control branching in a control string's target lists. If the function returns a function key that is not a value in the target list, Panther processes the key and executes its control string, if any.

Installation

You can install multiple functions as control functions. The following statements, typically found in funclist.c, include two control functions mark_low and mark_high in the fnc_data structure mark_funcs. To see the code for these functions, refer to page 44-87.

struct fnc_data mark_funcs[] = 
{
SM_OLDFNC( "mark_low", mark_low ),
SM_OLDFNC( "mark_high", mark_high ),
};
int markcount = sizeof ( mark_funcs )  
/ sizeof ( struct fnc_data ) ;

The following line of code, typically found in the function sm_do_uinstalls in funclist.c, installs the functions in mark_funcs as control functions:

sm_install ( CONTROL_FUNC, mark_funcs, &markcount ) ;

Status Line Function

Panther calls the status line function just before the status line is flushed or physically written to the terminal display. Because of delayed write, this might not coincide with calls to functions that specify message line text. You typically use this function for terminals that require special status line processing.

Arguments

The status line function gets no arguments. It can access copies of the text and attributes about to be flushed to the status line through the following calls to library functions:

stat_attr = sm_pinquire(SP_STATLINE);
stat_attr = sm_pinquire(status, SP_STATATTR);

Note that in the case of the status text and status attribute globals, sm_pinquire returns a pointer to a temporary copy of the arrays. You should copy these to a save location before using them.

Returns

If the status line function returns 0, Panther continues its usual processing and writes out the status line. If the function returns a non-zero value, Panther assumes that the function handles the physical write of the status line.

Installation

You can install only one status line function. The following statement, usually found in funclist.c, includes the status line function statln in the fnc_data structure stat_struct. To see the code for this function, refer to page 44-96.

struct fnc_data stat_struct = SM_OLDFNC( 0, statln ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs statln as the status line function:

sm_install ( STAT_FUNC, &stat_struct, (int *) 0 ) ;

Video Processing Function

A character-based application can use the video processing function for special handling of various video sequences. GUI applications ignore the video processing function. Use your own video processing function only if Panther has no video file that supports a specific terminal type. Panther's output function calls the video processing function just before it displays data on a Panther screen; consequently, this function should perform only low-level processing.

Video processing functions should not call Panther library functions.

Arguments

The video processing function receives two arguments:

Returns

If the function returns 0, normal processing is continued. If it returns a non-zero value is returned, Panther assumes that the function handled the operation. This lets you implement only necessary operations.

Installation

You can install only one video processing function. The following statement, usually found in funclist.c, includes the video processing function video in the fnc_data structure video_struct.

struct fnc_data video_struct = SM_OLDFNC( 0, video ) ;

The following line of code, usually found in the function sm_do_uinstalls in funclist.c, installs video as the video processing function:

sm_install ( VPROC_FUNC, &video_struct, (int *) 0 ) ;

Database Driver Hook Functions

Panther's database drivers have three hook functions that can be used to write database error handlers: ONERROR, ONENTRY, and ONEXIT. For more information about using these commands, refer to page 11-41 in the Programming Guide.


Transaction Manager Event Functions

When the transaction manager traverses the transaction tree in order to issue commands to each table view or server view, it checks to see if any of these table or server views have a function property specified. If so, the transaction manager looks for it among the installed functions and calls it. If the function contains processing for the current transaction event, that processing is completed.

If the return code is TM_PROCEED, the transaction manager then calls the database-specific and the common models for the same transaction event.

If the return code is TM_OK, the transaction manager continues to the next table view or the next transaction event. If the return code is TM_CHECK, TM_CHECK_ONE_ROW, or TM_CHECK_SOME_ROWS, the transaction manager pushes an event onto the stack to check for database errors.

For information about writing transaction event functions, refer to page 32-1.

Arguments

Transaction manager functions are passed a single integer argument that corresponds to the transaction event. Transaction events and their integer values are listed in tmusubs.h.

Returns

Table 44-3 summarizes possible return codes for transaction manager functions:

Table 44-3 Return codes for transaction manager functions

Return value Description

TM_OK

Event processing succeeded.

TM_FAILURE

Event processing failed.

TM_PROCEED

After completing the function, proceed to call the transaction model for this event, as if this function had never been called.

TM_CHECK

Test to see if an error occurred. This is used in data base-specific transaction models to check for SQL execution errors.

TM_CHECK_ONE_ROW

In addition to an error test, test that exactly one row was affected by the processing.

TM_CHECK_SOME_ROWS

In addition to an error test, test that one or more rows were affected by the processing.

TM_UNSUPPORTED

Event was not recognized.

Installation

You can specify a transaction manager function for each table view or server view in a screen for insert, update, or delete processing. With the table view selected, enter the name of the function in the Function property (under Transaction). If the function affects the SQL generation of SELECT statements, the function must be entered on the appropriate server view.

The function itself can be either a JPL procedure or C function that is installed in the prototyped function list.

Errors

When a screen is opened, the transaction manager reports an error if the function cannot be found. As a result of this error, the transaction manager does not start its transaction.


Sample Functions

The following sections show sample code for commonly used function types.

Prototyped

This section has two sample functions:

For example, this control string highlights all values on the screen between zero and 500:

^mark_flds (0, 500)

The next control string highlights all values on the screen that are greater than 1000 or less than -300:

^mark_flds (1000, -300)

The following code comprises the entire mark_flds function.

/* Include Files */ 
#include "smdefs.h" /* screen manager Header File */
#include "smglobs.h" /* screen manager Globals */
/* Macro Definitions... */ 
/* Attributes used to mark fields */
#define MARK_ATTR REVERSE | HILIGHT | BLINK 

int
mark_flds ( bound1, bound2 )
int bound1 ; /* First Boundary on fields to mark */ 
int bound2 ; /* Second Boundary on fields to mark */
{ 
int fld_num ; /* Field Number */
char *fld_data; /* Field Data */
double fld_val ; /* Field Value */
int num_of_flds ;/* Number of Fields */
int *old_attrib ;/* Array of old attributes */
   /* Determine number of fields */
   num_of_flds = sm_inquire ( SC_NFLDS ) ;
   /* Allocate memory for attribute array */
   old_attrib = (int *)calloc ( num_of_flds, 
sizeof ( int ) ) ;
   /* Cycle through all the fields on the screen */
   for ( fld_num = 1 ; fld_num <= num_of_flds ; fld_num++ ) 
{
/* Store away old attributes */
      old_attrib[fld_num-1] =  
sm_finquire ( fld_num, FD_ATTR ) ;
      /* Make sure it is a field with numbers */
      fld_data = sm_strip_amt_ptr ( fld_num, NULL ) ; 
if ( ! *fld_data ) continue ;
      /* Create a double from it */
      fld_val = sm_dblval( fld_num ) ;
      /* See if fld_val is in bounds */
      if ( bound1 <= bound2 ) 
{
/* Mark fields between bounds. */
         if ( ( fld_val >= ( double )bound1 ) && 
( fld_val <= ( double )bound2 ) )
{
sm_chg_attr ( fld_num,
MARK_ATTR ) ;
}
}
else
{
         /* Mark fields outside bounds. */
         if ( ( fld_val >= ( double )bound1 ) || 
( fld_val <= ( double )bound2 ) )
{
sm_chg_attr ( fld_num, MARK_ATTR ) ;
}
}
}
   /* Wait for acknowledgement */
   sm_ferr_reset ( "Hit <space> to continue") ;
   /* Cycle again through all the fields on the screen */
   for ( fld_num = 1 ; fld_num <= num_of_flds ; fld_num++ ) 
{
/* Reset field attributes */
      sm_chg_attr ( fld_num,  
old_attrib[ fld_num - 1 ] ) ;
   }
   /* Release memory */
   free ( (char *)old_attrib ) ; 
return ( 0 ) ;
}

Example 2

report generates a report whose type and output device vary according to the supplied arguments. This function takes two string arguments:

If the second argument starts with an exclamation point (!), the remainder is interpreted as an operating system command. The report is created in a temporary file, and the name of the file is passed as an argument to the operating system command. If a tilde (~) is embedded in the command, the name of the temporary file is substituted for the tilde, otherwise the name is just appended at the end. These two control strings both cause a screen report to print on a UNIX system:

^report ("screen","!lp -c -s")
^report ("screen", "!date | cat - ~ | lp -s")

If the second argument starts with a vertical bar (|), the remainder is also interpreted as an operating system command. In this case, however, the report is piped into the standard input of that command. This control string prints out the last twenty lines of a window stack report on a UNIX system:

^report ("wstack", "| tail | lp -s")

Finally, if the second argument is a valid file name, the report is appended to the named file. This control string causes a display terminal report to be appended to the file report.fil:

^report("term", "report.fil")

The following code comprises the entire report function.

/* Include Files */ 
#include "smdefs.h" /* screen manager Header File */
#include "smglobs.h" /* screen manager Globals */
int
report ( report_type, report_out ) 
char *report_type ; /* Type of report: field, screen,
wstack, or term. */
char *report_out ; /* Output designation. */
{ 
char *fn = NULL ; /* Name of output file */
char *ptr, *ptr1 ; /* Character pointers */
char msg_buf[ 128 ]; /* Message buffer */
FILE *fp ; /* File pointer for output */
int size ; /* Size of output file */
int cur_no ; /* Current field number */
int select ; /* Current window stack index */
   /* If an output designation was made... */
   if ( report_out && *report_out ) 
{
/* Based on what output type we designated: */
      switch ( *report_out )
      { 
case '!' :
/* OS command. Open temp file */
fn = tempnam ( NULL, "rprt" ) ;
fp = fopen ( fn, "w" ) ;
break ;
       case '|' :
         /* Pipe. Open the pipe */
         fp = popen ( report_out + 1,  
"w" ) ;
break ;
      default :
         /* Other. Open the file */
         fp = fopen ( report_out, "a+" ) ; 
break ;
      }
      /* If we could not open the file, show error */
      if ( ! fp ) 
{
sprintf ( msg_buf,
"Cannot open stream for %s.",
report_out ) ;
sm_ferr_reset ( msg_buf ) ;
return ( -1 ) ;
}
}
   /* If no report output specified, open temp file for 
storing message window stuff. */
   else 
{
fn = tempnam ( NULL, "rprt" ) ;
fp = fopen ( fn, "w+" ) ;
report_out = "" ;
}
   fprintf ( fp, "  REPORT TYPE: %s ", report_type ) ;
   /* Now, based on the report_type, which is the name 
with which the function was invoked, create
the reports. Note that all newlines are
preceded with spaces, this is so that in the
case of the message windows we can replace
all space-newlines with %N, the newline
indicator for Panther windows. */
   switch ( *report_type ) 
{
case 'F':
case 'f':
/* Output a field report */
      fprintf ( fp, " Field Report: " ) ;
      /* Field Identifier and contents */
      cur_no = sm_getcurno ( ); 
fprintf ( fp, "FIELD: %d (%s[%d]) = %s ",
cur_no,
sm_name ( cur_no ),
sm_occur_no ( ),
sm_fptr ( cur_no ) ) ;
      /* Field sizes */
      size = sm_finquire ( cur_no, FD_LENG ) ; 
fprintf ( fp, "LENGTH: onscreen: %d "
"Max: %d ",
size, sm_finquire ( cur_no,
FD_SHLENG )
+ size ) ;
      fprintf ( fp, "# OCCURRENCES: onscreen: %d " 
"Max: %d ",
sm_finquire ( cur_no, FD_ASIZE ),
sm_max_occur ( cur_no ) ) ;
      break;
   case 'S': 
case 's':
      /* Output screen report */
      fprintf ( fp, "  Screen Report: " ) ;
      /* Screen Name */
      fprintf ( fp, "SCREEN: %s ", 
sm_pinquire ( SP_NAME ) ) ;
      /* How much of screen is visible */
      fprintf ( fp, "%% VISIBLE IN VIEWPORT: %d ", 
100 *
( sm_inquire ( SC_VNLINE ) *
sm_inquire ( SC_VNCOLM ) ) /
( sm_inquire ( SC_NCOLM ) *
sm_inquire ( SC_NLINE ) ) ) ;
      break ;
   case 'w': 
case 'W':
      /* Output Window stack report */
      fprintf ( fp, "  Window Stack Report: " ) ;
      /* Cycle through all the windows. */
      for ( select = 0 ;  
sm_wselect ( select ) == select ;
select++ )
{
/* Window number... */
         fprintf ( fp, " Window %d: ",  
select ) ;
         /* Screen name */
         fprintf ( fp, "Screen: %s ", 
sm_pinquire ( SP_NAME ) ) ;
         /* Number of fields and groups */
         fprintf ( fp, "# of Fields: %d " 
"# of Groups: %d ",
sm_inquire ( SC_NFLDS ),
sm_inquire ( SC_NGRPS ) ) ;
sm_wdeselect ( ) ;
}
sm_wdeselect ( ) ;
break ;
   case 'T':
   case 't':
      /* Output display terminal report */
      fprintf ( fp, "  Terminal Report: " ) ;
      /* Terminal Type */
      fprintf ( fp, "TERM TYPE: %s ", 
sm_pinquire ( P_TERM ) ) ;
      /* Display mode */
      if ( sm_inquire ( I_NODISP ) ) 
fprintf ( fp, "DISPLAY OFF " ) ;
else
fprintf ( fp, "DISPLAY ON " ) ;
      /* Input mode */
      if ( sm_inquire ( I_INSMODE ) ) 
fprintf ( fp, "INSERT MODE " ) ;
      else 
fprintf ( fp, "TYPEOVER MODE " ) ;
      /* Block mode */
      if ( sm_inquire ( I_BLKFLGS ) ) 
fprintf ( fp, "BLOCK MODE " ) ;
      /* Physical display size */
      fprintf ( fp, "DISPLAY SIZE: %d x %d ", 
sm_inquire ( I_MXLINES ),
sm_inquire ( I_MXCOLMS ) ) ;
break;
   default:
      /* Unrecognized report type */
      fprintf ( fp, "Illegal Report Type  " ) ; 
return ( -3 ) ;
}
   /* Once again, based on the type output... */
   switch ( *report_out ) 
{
case '|' :
      /* It was a pipe, so close it. */
      pclose ( fp ) ; 
sm_ferr_reset ( "Pipe successful" ) ;
break ;
   case '!' :
      /* It was an O/S command. Close file... */
      fclose ( fp ) ;
       /* Gobble up the exclamation point */
      report_out++;
      /* Look for tildes */
      if ( ptr = strchr ( report_out, '~' ) ) 
{
/* Found the tilde. Substitute the
file name for it. */
         *ptr = ''; 
sprintf ( msg_buf, "%s%s%s",
report_out, fn, ptr+1 ) ;
}
      else
      { 
/* No tilde. Append file name to
O/S command. */
         sprintf ( msg_buf, "%s %s",  
report_out, fn ) ;
}
      /* Do the command. */
      system ( msg_buf ) ;
      /* Delete temp file and free its name. */
      remove ( fn ) ; 
free ( fn ) ;
sm_ferr_reset ( "Command Invoked" ) ;
break ;
   case '':
      /* Message window. Get size of file... */
      size = ftell ( fp ) ;
      /* Allocate memory for it. */
      ptr = malloc ( size + 1 ) ;
      /* Rewind the file */
      fseek ( fp, SEEK_SET, 0 ) ;
      /* Read it into the malloced buffer. */
      fread ( ptr, sizeof ( char ), size, fp ) ;
      /* Close and delete file, free file name */
      fclose ( fp ) ; 
remove ( fn ) ;
free ( fn ) ;
      /* null terminate memory buffer of report */
      ptr[size] = '';
      /* Replace all space-newlines with %N */
      for ( ptr1 = ptr ; 
ptr1 = strchr ( ptr1, '' ) ;
ptr1++ )
{
ptr1[-1]='%';
ptr1[0]='N';
}
      /* Pop up the message window */
      sm_message_box  
( ptr, 0, SM_MB_OK|SM_MB_ICONNONE, 0) ;
      /* Free up the malloced buffer. */
      free ( ptr ) ; 
break ;
   default :
      /* File appended, just close it. */
      fclose ( fp ) ; 
sm_ferr_reset ( "File appended" ) ;
break ;
}
return ( 0 ) ;
}

Automatic Screen

The following screen function, intended as the application's automatic screen function, maintains information on how long screens are open, and the total amount of time they are active. Note the use of the P_USER pointer, a general purpose pointer that you can manipulate, which is associated with an open screen.

This function keeps track of the length of time that the user has spent with a screen open and active. It is intended to be installed as the default screen function for an application. Note that in the example, the times are shown on the status line, but they could be logged to a file for time management analysis.

For this function to operate correctly, the setup variable EXPHIDE_OPTION must be set to ON_EXPHIDE, so Panther calls functions on screen overlay and reexposure.

The time() call used in this function is ANSI C. On UNIX platforms it returns the number of seconds elapsed since January 1, 1970, GMT.

/* Include Files */ 
#include "smdefs.h" /* screen manager Header File */
#include "smglobs.h" /* screen manager Globals */
#include <time.h> /* ANSI time() Header File */
/* Data structure to hold aggregate times by screen */
struct my_info 
{
time_t opentime ; /* Time screen was opened */
time_t acttime ; /* Time screen was activated */
double usedtime; /* Aggregate time active */
double totaltime ;/* Aggregate time open */
};
int 
auto_sfunc ( name, context )
char *name ; /* Screen Name */
int context ; /* Context for function call */
{ 
struct my_info *my_info_ptr ; /* Time buf pointer */
char *action_verb = /* Text of context */
"inspecting" ;
time_t current_time ;
int do_free = 0 ; /* Flag, set to free
memory */
   char msg_buf[ 128 ] ;          /* Message buffer */
   /* 
* We make assumptions here: screens that are not named
* are unimportant and should not have logging done.
* This will exclude dynamically created message
* windows.
*/
   if ( ( ! name ) || ( ! *name ) ) 
{
return ( 0 ) ;
}
   /* Get the current time. ( ANSI Standard call ) */  
current_time = time ( (time_t *)0 ) ;
   /* Get the pointer to time structure  
associated with this screen */
my_info_ptr = (struct my_info *)sm_pinquire ( P_USER ) ;
    /* Figure out which context we are called in. */ 
if ( context & K_ENTRY )
{
if ( context & K_EXPOSE )
{
/*
* Screen exposed (activated) when
* overlying window was closed.
* Set context string verb and
* add to the aggregate open time.
*/
         action_verb = "activating" ; 
my_info_ptr->totaltime =
my_info_ptr->totaltime +
difftime ( current_time,
my_info_ptr->opentime ) ;
  
}
      else 
{
/* Screen opened. */
action_verb = "opening" ;

/* Allocate memory for time structure */
my_info_ptr =
(struct my_info *)
malloc ( sizeof (
struct my_info ) ) ;
if ( ! my_info_ptr )
{
sm_ferr_reset ( "No memory" ) ;

sm_cancel ( 0 ) ;
}
         /* Associate the buffer with screen */ 
sm_pset ( P_USER, (char *)my_info_ptr ) ;
         /* Set initial time values */ 
my_info_ptr->opentime = current_time ;
my_info_ptr->usedtime = 0 ;
my_info_ptr->totaltime = 0 ;
}
      /* Set initial value of aggregate active time */ 
my_info_ptr->acttime = current_time ;
}
else
{
if ( context & K_EXPOSE )
{
/* Screen overlaid with window. */
action_verb = "deactivating" ;
}
      else 
{
/* Screen closed. */
action_verb = "closing" ;
/* Set flag to free the time structure */
         do_free = 1 ;
      }
      /* Calculate new aggregates. */
      my_info_ptr->usedtime = 
my_info_ptr->usedtime +
difftime ( current_time,
my_info_ptr->acttime ) ;
      my_info_ptr->totaltime = 
my_info_ptr->totaltime +
difftime ( current_time,
my_info_ptr->opentime ) ;
   }
   /* Format the message. */
   sprintf ( msg_buf, "Now %s screen %s." 
" Seconds active: %.1f."
" Seconds open: %.1f.",
action_verb, name,
my_info_ptr->usedtime,
my_info_ptr->totaltime ) ;
   /* If time structure memory should be freed, free it. */
   if ( do_free ) 
{
free ( my_info_ptr ) ;
}
   /* Output the message. Could be to log file,  
here it is to stat line */
   sm_ferr_reset ( msg_buf ) ;
   return ( 0 ) ; 
}

Automatic Widget

This section has two sample functions:

Example 1

This function puts general information about the current widget on the status line. This function is installed as the automatic widget function in a Panther application; it is called on entry, exit, and validation for all widgets.

On widget entry, the function places information about the widget on the status line:its name, if any, number, and occurrence offset. If the widget is a selected member of a group—for example, radio buttons or a list box—the status line shows the text of the selected widget, the group name, and the group occurrence.

/* Include Files */ 
#include "smdefs.h" /* screen manager Header File */
int 
auto_ffunc ( f_number, f_data, f_occurrence, context )
int f_number ; /* Field Number */
char *f_data ; /* Field Data */
int f_occurrence ; /* Array Index */
int context ; /* Context Bits */
{ 
char *f_name ; /* Field Name */
char *g_name ; /* Group Name */
char *slct ; /* selected or deselected */
int g_occurrence ; /* Group Number */
char stat_line[ 128 ];/* Status line string */
   /* If called on field exit, clear the status line. */ 
if ( context & K_EXIT )
{
sm_setbkstat ( "", WHITE ) ;
}
   /* If called on entry, format and display status line */ 
else if ( context & K_ENTRY )
{
      /* Obtain the field name */  
f_name = sm_name ( f_number ) ;
      /* Format the status line */  
if ( f_name && *f_name)
sprintf ( stat_line, "Current Field: "
"%s[%i] ( #%i[%i] )",
f_name, f_occurrence,
f_number, f_occurrence ) ;
else
sprintf ( stat_line,
"Current Field: #%i[%i]",
f_number, f_occurrence ) ;
      /* Display the status line */ 
sm_setbkstat ( stat_line, BLUE | HILIGHT ) ;
}
   /*  
* If we get here, it is neither entry nor exit so it must
* be validation. In this case, see if the field is the
* member of a group. If it is, the validation function
* was called because the field was selected, or in the
* case of checklists, deselected. Note that
* menu selection events will not be flagged, because
* menus are not groups.
*/
   else if ( g_name = sm_o_ftog ( f_number,  
f_occurrence,
&g_occurrence ) )
   { 
/* Determine if selected or deselected */
      if ( sm_isselected ( g_name, g_occurrence ) ) 
slct = "selected" ;
else
slct = "deselected" ;
      /* Format and print status line message */
      sprintf ( stat_line, "%s %s, group %s[%d]", 
f_data, slct, g_name, g_occurrence ) ;
      sm_setbkstat ( stat_line, BLUE | HILIGHT ) ; 
}
   /* Return code of zero means that everything is fine. */
   return ( 0 ) ; 
}

Example 2

This second example of an automatic widget function shows how to use a widget's memo edits to pass non-standard information to that function. This function validates each widget against the contents of its memo edits.

This function is to be installed as a non-prototyped widget validation function in a Panther application, either on the

FIELD_FUNC list or as the 
DFLT_FIELD_FUNC.
The function validates widgets according to a list of values that are found in the first memo text edit. Possible values in the memo text edit are separated by spaces.
/* Include Files */
#include "smdefs.h"       /* screen manager Header File */
int 
memoval ( f_number, f_data, f_occurrence, context )
int f_number ; /* Field Number */
char *f_data ; /* Field Data */
int f_occurrence ; /* Array Index */
int context ; /* Context Bits */
{ 
char *memo_text ; /* Memo text string */
char *token_ptr ; /* Token */
char msg[ 128 ] ; /* message string */

/* If called on field entry or exit, or if already
validated, or if empty, just exit right off. */
   if ( ( context & K_EXIT  ) || 
( context & K_ENTRY ) ||
( context & VALIDED ) ||
( ! *f_data ) )
   { 
return ( 0 ) ;
}
   /* Get the first memo text edit string. */
   if ( ! ( memo_text = sm_edit_ptr ( f_number, MEMO1 ) ) ) 
{
/* There is no memo text edit string. */
return ( 0 ) ;
}
   /* Duplicate the string. (Note: pass over the two length 
bytes returned by sm_edit_ptr) */
   if ( ! ( memo_text = strdup ( memo_text + 2 ) ) ) 
{
/* Memory allocation error. */
return ( 0 ) ;
}
   /* Cycle down the memo text string grabbing tokens.  
If we have a match, break out of loop. */
   for ( token_ptr = strtok ( memo_text, " " ) ; 
token_ptr && strcmp ( token_ptr, f_data ) ;
token_ptr = strtok ( NULL, " " ) ) ;
   /* Free up memory. */ 
free ( memo_text ) ;
   /* If we found matching token, validate OK. */ 
if ( token_ptr )
return ( 0 ) ;
   /* Error condition. Create error string. */
   sprintf ( msg, "Invalid value %s in field. " 
"Valid values are: %s.", f_data,
sm_edit_ptr ( f_number, MEMO1 ) + 2 ) ;
   sm_ferr_reset ( 0, msg ) ;
   /* Return and reset cursor. */
   return ( 2 ) ; 
}

Demand Widget

The following local widget functions can be called by individual widgets to perform initialization and validation based on external criteria:

Two widget functions to include on the widget function list are defined here. The first one, fentry, initializes the value in a widget provided that it has not changed since the screen was opened. The second one, fvalid, validates the contents of a widget. The functions that retrieve the initialization data and lookup the validation data are externally defined and are application-specific.

/* Include Files */
#include "smdefs.h"   /* screen manager Header File */
 
/* Externally defined functions */ 
extern char *do_my_initialize ( ) ; /* Get data for field
initialization */
extern int my_lookup ( ) ; /* Lookup data for field
validation */
int 
fentry ( f_number, f_data, f_occurrence, f_context )
int f_number ; /* Field Number */
char *f_data ; /* Field Data */
int f_occurrence ; /* Array Index */
int f_context ; /* Context bits */
{ 
/* Initialize if the field has not been modified
since the screen was opened. */
   if ( ! ( f_context & MDT ) ) 
{
sm_putfield ( f_number, do_my_initialize ( ) ) ;
}

return ( 0 ) ;
}

int
fvalid ( f_number, f_data, f_occurrence, f_context )
int f_number ; /* Field Number */
char *f_data ; /* Field Contents */
int f_occurrence ; /* Occurrence number for field */
int f_context ; /* Context bitmask */
{ 
char msg_buf[ 80 ];/* Message line buffer */

/* If the field is already valid, merely return. */
if ( f_context & VALIDED )
return ( 0 ) ;
   /* If the field is invalid based on external  
lookup, return error. */
if ( my_lookup ( f_data ) )
{
/* Error, so reposition field. */
      sm_gofield ( f_number ) ;
      sprintf ( msg_buf, "Invalid data %s.", f_data ) ; 
sm_ferr_reset ( 0, msg_buf ) ;
      /* Return code of 1 indicates validation fail */
      return ( 1 ) ; 
}
return ( 0 ) ;
}

Automatic Group

The group function auto_gfunc is installed as the automatic group function—that is, a function that is called whenever group entry, exit, or validation occurs. On entry, this function installs the keychange function keychg, which lets users select group widgets by pressing the X key. On group exit, auto_gfunc deinstalls keychg.

Note that preexisting keychange functions should be stacked by auto_gfunc. keychg also chains existing keychange functions along, but it is assumed that they are written in C. Preexisting keychange functions in some other supported 3GL language may not be properly chained by this function.

For a more extended example of keychange functions, see page 44-79.

/* Include Files */
#include "smdefs.h" /* screen manager Header File */
#include "smkeys.h" /* screen manager Logical Keys */
static int keychg ( ) ;
static struct fnc_data o_keychg ; /* Old keychg */
static struct fnc_data *fnc_ptr ; /* Event Pointer */
static struct fnc_data keychg_struct/* New keychg */
= { 0, keychg, 0, 0, 0, 0 };
int 
auto_gfunc ( name, context )
char *gp_name ; /* Group Name */
int context ; /* Context bits */
{
/* If called on group entry.... */
if ( context & K_ENTRY )
{
/* Install the new keychange function */
fnc_ptr = sm_install ( KEYCHG_FUNC,
&keychg_struct,
(int *)0 ) ;
      /* If there was an old one, store it away. */ 
if ( fnc_ptr )
{
memcpy ( (char *)&o_keychg,
(char *) fnc_ptr,
sizeof ( struct fnc_data ) ) ;
}
      else 
{
memset ( (char *)&o_keychg, 0,
sizeof ( struct fnc_data ) ) ;
}
}
   /* If called on group exit...... */ 
else if ( context & K_EXIT )
{
/* If there was an old keychange function */
if ( fnc_ptr )
{
/* Re-install it. */
sm_install ( KEYCHG_FUNC, &o_keychg,
(int *)0 ) ;
}
      else 
{
/* Get rid of the current one anyway. */
sm_install ( KEYCHG_FUNC, NULL,
(int *) 0 ) ;
}
}
return ( 0 ) ;
}
static int 
keychg ( key )
int key ;
{ 
/* If there was an old keychange function ..... */
if ( o_keychg.fnc_addr )
{
/* Chain the old keychange function. */
key = ( o_keychg.fnc_addr )( key ) ;
      /* WARNING: This is not completely general, since 
old keychange functions not written in C
may not be called properly. */
   }
   /* 
* Now do the new keychange. Basically, we want to select
* group members by typing "x", move the cursor to the
* next group member immediately after selection, and have
* the NL key move to the next selection.
*/
   switch ( key ) 
{
case 'x' :
case 'X' :
key = NL ;
break ;
   case NL : 
key = ' ' ;
break ;
}
   return ( key ) ;
}

External Help

sm_PiXmDynaHook is a help function that Panther uses to invoke its own help facilities. Use the External Help Tag property of screens, menus, and screen-resident widgets to specify help context identifiers. This identifier is passed to the help function when the user invokes help from an application component.

The following help driver is supplied with Panther; it invokes context-sensitive help from the screen editor.

/*** sample client code for dyna help server ***/ 
/** Includes **/
#include "smmach.h"
#include "smproto.h"
#include "smxmuser.h"
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/StringDefs.h>
#include "xmhelphk.h"
/* typedef */ 
typedef struct PiXmDynaPath_s PiXmDynaPath_t;
struct PiXmDynaPath_s
{
char *pszDynaCollection;
char *pszDynaBook;
};
/** statics **/
static Atom xaServer = (Atom)0;
static Atom xaRequest = (Atom)0;
static PiXmDynaPath_t dynaPath = {NULL, NULL};
static XtResource xresDyna[] =
{ 
{ "helpPath", "HelpPath", XtRString, sizeof(char *),
XtOffsetOf(PiXmDynaPath_t, pszDynaCollection),
XtRString,
"/u/apps/ebt22" },
{ "editorHelpFile", "EditorHelpFile", XtRString,
sizeof(char *), XtOffsetOf
(PiXmDynaPath_t, pszDynaBook), XtRString, "editors" },
};
int sm_PiXmDynaHook PROTO((char *));
static int PiXmInitHelp PROTO((Display *));
static void PiXmSendMsg PROTO((Display *, Window, char *));
/*
NAME
The event function to the Dyna Server. Takes "Tag" string
and makes a help server request. However if the "Tag" is
the string "SM_RESERVED_QUIT_TAG" then server is killed.
SYNOPSIS
iRetVal = sm_PiXmDynaHook(pszTag);
char *pszTag; The "Tag" identification string
int iRetVal;
DESCRIPTION
0) If tag is "SM_RESERVED_QUIT_TAG" the quit. Otherwise.
1) Get the path of the help file
2) Initialize the server (if needed)
3) Build the server request
4) send the request
RETURNS 
Returns
PI_ERR_NONE on success
PI_ERR_NO_MORE on failure
*/
int 
sm_PiXmDynaHook PARMS((pszTag))
LASTPARM(char *pszTag)
{ 
char pszMessage[512];
Display *dpy = sm_xm_get_display();
Boolean bQuiting = strcmp(pszTag, "SM_RESERVED_QUIT_TAG");
	if (dynaPath.pszDynaBook == NULL) 
{
XtGetApplicationResources(sm_xm_get_base_window(),
&dynaPath, xresDyna, XtNumber(xresDyna), NULL, 0);
}
		if (!PiXmInitHelp (dpy)) 
return(PI_ERR_NO_MORE);
sprintf(pszMessage,
"command=ebt-link collection=%s book=%s
target=ancestor(ancestor(idmatch('Tagname','%s')))
stylesheet=fulltext.v showtoc=true",
dynaPath.pszDynaCollection, dynaPath.pszDynaBook,
pszTag);
PiXmSendMsg (dpy, DefaultRootWindow(dpy), pszMessage);
return(PI_ERR_NONE);
}
/*
NAME 
PiXmInitHelp - Start the server if needed. Set server atoms.
SYNOPSIS 
iRetVal = PiXmInitHelp(dpy);
Display *dpy;
int iRetVal;
DESCRIPTION 
Start the server if needed. Set the server atoms.
RETURNS 
Returns
PI_ERR_NONE on success
PI_ERR_NO_MORE on failure
*/
static 
int
PiXmInitHelp PARMS((display))
LASTPARM(Display *display)
{ 
int iPid;
int iDummy;
char *pszShellPath; /* pointer to SHELL environment var */
char *pszShell; /* the last segment of the path */
void (*pfuncIntr)(), (*pfuncQuit)(), (*pfuncTstp)();
char *server_name = "SM_JAM_DYNA_HELP_SERVER";
char *selection_name = "SM_JAM_DYNA_HELP_SELECTION";
char *request_name = "SM_JAM_DYNA_HELP_REQUEST";
int iRetVal = 1;
static Boolean bCreatedServer = FALSE;
XInternAtom (display, selection_name, False);
xaServer = XInternAtom (display, server_name, False);
if (!bCreatedServer)
XSetSelectionOwner(display, xaServer, None,
CurrentTime);
if ((!bCreatedServer) || XGetSelectionOwner(display,
xaServer) == None)
{
#ifdef SIGTSTP
#define SIGNAL(a,b) signal(a,b)
pfuncTstp = SIGNAL(SIGTSTP, SIG_DFL);
#else
#define SIGNAL(a,b)
#endif
			/* see if there is a SHELL variable */ 
pszShellPath = getenv ("SHELL");
if (!pszShellPath || !pszShellPath[0])
pszShellPath = "/bin/sh";
if (!(iPid = fork()))
{
pszShell = strrchr(pszShellPath, '/');
if (!pszShell || !pszShell[1])
pszShell = pszShellPath;
execlp (pszShellPath, pszShell, "-c",
"xmjxhelp", (char *)0);
exit (-1);
}
}
		bCreatedServer = TRUE; 
pfuncIntr = signal(SIGINT, SIG_IGN);
pfuncQuit = signal(SIGQUIT, SIG_IGN);
while (XGetSelectionOwner(display, xaServer) == None)
{
if(waitpid(iPid, &iDummy, WNOHANG))
{
/* server failed */
iRetVal = PI_ERR_NO_MORE;
break;
}
		else 
{
sleep(1);
}
}
		signal (SIGINT, pfuncIntr); 
signal (SIGQUIT, pfuncQuit);
SIGNAL (SIGTSTP, pfuncTstp);
xaRequest = XInternAtom(display, request_name, False);
return(iRetVal);
}
/*
NAME 
PiXmSendMsg - Send the msg request to the help server.
SYNOPSIS 
PiXmSendMsg(dpy, xwin, pszMsg);
Display *dpy;
Window xwin;
char *pszMsg;
DESCRIPTION 
Send the msg request to the help server.
*/
static 
void
PiXmSendMsg PARMS((dpy, xwin, pszMsg))
PARM(Display *dpy)
PARM(Window xwin)
LASTPARM(char *pszMsg)
{
XChangeProperty(dpy, xwin, xaRequest, XA_STRING, 8,
PropModeReplace, (unsigned char *) pszMsg,
strlen(pszMsg)+1);
XConvertSelection(dpy, xaServer, XA_STRING, xaRequest,
xwin, CurrentTime);
XFlush(dpy);
}

Timeout

screen_saver is a timeout function that acts as a screen saver that is invoked after ten minutes of keyboard inactivity. The same function restores the screen when a key is typed.

/* Include files */
#include "smdefs.h"
int screen_saver( int why_called )
{
if ( why_called == TF_TIMEOUT ) /*clear the screen
after timeout */
   {
sm_clrviscreen ( );
}
else if ( why_called == TF_RESTART ) /*restore screen
after key hit */
   {
sm_rescreen ( );
}
   /* Returning STOP_CALLING means this function is not
* called again every ten minutes
*/
   return ( TF_STOP_CALLING )
}

Key Change

The following key change function intercepts each occurrence of the user entering an exclamation point or striking the EXIT key.

This application keychange function causes sm_getkey to intercept two keys, the exclamation point and the logical EXIT key. When the user types an exclamation point, this function asks if an operating system shell is wanted. If so, a shell is provided. If the user types EXIT, the function ensures that the user really wants to EXIT before returning the EXIT back to sm_getkey.

Note that if the user escapes to the shell or does not want to EXIT, the keychange function swallows the keystroke. If the user does not want the shell or wants to EXIT, the keystroke is passed back to sm_getkey.

Note also preprocessor directives on whether or not the Panther executive is in use. If the executive is in use, we do not query about the EXIT if there are control strings associated with EXIT. Also, we can use the standard Panther operating system escape.

/* Include Files */ 
#include "smdefs.h" /* screen manager Header File */
#include "smkeys.h" /* screen manager Logical Keys */
#define EXIT_CONFIRM "Do you want to EXIT? (y/n)" 
#define SHELL_CONFIRM "Do you want to go to OS? (y/n)"
int keychg ( int the_key )/* Key read from keyboard by  
* sm_getkey */
{ 
static int recursive ; /* Flag ensuring no recursion. */
   /* First ensure that we are not called recursively */ 
if ( recursive ) return ( the_key ) ;
   /* Set recursive flag */ 
recursive++ ;
   /* Based on the key read from the keyboard..... */ 
switch ( the_key )
   { 
case EXIT:
/*
* If the read key is an EXIT, make sure that there are
* no control strings associated with EXIT and confirm
* that the user really wants to EXIT. If the user does
* not want to, set the key to zero. The PROL_EXECUTIVE
* macro is not defined in any Panther header file. It
* is used here to distinguish between applications that
* use the Panther executive and those that don't.
*/
if (
#ifdef PROL_EXECUTIVE
! sm_getjctrl ( EXIT, 0 ) &&
! sm_getjctrl ( EXIT, 1 ) &&
#endif
( sm_query_msg ( EXIT_CONFIRM )
== 'n' ) )
{
the_key = 0 ;
}
break ;
   case '!': 
/*
* If the read key is an exclamation point, confirm
* that the user really wants to escape to the shell
* If so, escape to the shell and gobble up the key.
* If not, merely pass the key on back
*/
if ( sm_query_msg ( SHELL_CONFIRM ) == 'y' )
{
sm_leave ( ) ;
         /* SHELL UNDER UNIX */ 
system ( "sh -i" ) ;
         sm_return ( ) ; 
sm_rescreen ( ) ;
         the_key = 0 ; 
}
break ;
}
   /* Clear the recursion flag. */ 
recursive = 0 ;
   /* Pass the key back up. (If it is changed to zero, 
we gobbled it.) */
return ( the_key ) ;
}

Error

The following error function writes all user messages to a log file.

#include <smdefs.h>
/* log all messages sent to the user to the file "err.txt" */
int myerr(int msgno, char *msgtxt, int quiet_mode) 
{
FILE *fp;
    /* by default, use 'msgtxt' param & no prepended 
* "ERROR " string
*/
char *err_msg = msgtxt;
char *quiet_txt = "";
    /* if msgno != -1, retrieve msg text from msg file */  
if (msgno != -1)
err_msg = sm_msg_get(msgno);
    /* if called via the 'quiet' variety of error function */ 
if (quiet_mode)
quiet_txt = "ERROR: ";
    fp = fopen("err.txt", "a+");
    if (fp == NULL) { 
perror("error opening 'err.txt'");
exit(1);
}
    fprintf(fp, "myerr: %s'%s'", quiet_txt, err_msg);
    fclose(fp); 
return 0;
}

Insert Toggle

The following example shows a function that displays the current insert/overwrite mode at the end of the status line. This function is installed as the INSCRSR_FUNC function, called whenever Panther moves from insert to overstrike mode or vice versa. The status line displays INS when in insert mode, and OVR when in overstrike mode.

This routine assumes that cursor position display is not in use. You may also need a STAT_FUNC function for this, as Panther overwrites the status line with messages, thus destroying the INS/OVR message.

The last column of the status line is not written; Panther does not permit writing to the last position of a screen if it causes automatic hardware scrolling.

/* Include Files */
#include "smdefs.h"  /* screen manager Header File */
/* Buffer Sizes */
#define STAT_LINE_LEN 80
int inscrsr ( int enter_ins_mode ); 
/* enter_ins_mode is non-zero if about to
* enter insert mode, zero if about
* to enter overstrike mode
*/
{
if ( enter_ins_mode )
sm_d_msg_line ( "INS", 0) ;
else
sm_d_msg_line ( "OVR", 0) ;
return ( 0 ) ;
}

Initialization and Reset

The following code shows an example of initialization and reset functions. Note that most of the initialization need not be done in the initialization event. It could be done before sm_initcrt is called.

The two functions below, uinit and ureset, are to be installed as the initialization and reset functions respectively.

Note that ssignal is ANSI C. The signals SIGINT, SIGABRT, and SIGTERM are all part of ANSI C and the posix standard, and are meaningful on most but not all platforms.

/* Include Files */
#include "smdefs.h"   /* screen manager Header File */ 
#include <signal.h> /* software signals */
 static time_t start_time ; /* Application start time */
int 
uinit ( term )
char * term ; /* 30-byte buffer with terminal type */
{
char * ptr ;
   /* Determine current time as starting time. */ 
start_time = time ( (time_t*)0 ) ;
   /* Get terminal type from user. (If nothing entered, 
system will use the environment.) */
printf ( "Please enter terminal type: " ) ;
   if ( ! fgets ( term , 29 , stdin ) ) * term = '' ; 
term[ 29 ] = '' ;
   if ( ptr = strchr ( term , '' ) ) * ptr = '' ;
   /* Establish necessary signal handling. */ 
ssignal ( SIGINT , sm_cancel ) ;
ssignal ( SIGABRT , sm_cancel ) ;
ssignal ( SIGTERM , sm_cancel ) ;
return ( 0 ) ;
}
int 
ureset ( )
{
int hours , minutes , seconds ;
   /* Determine elapsed time since start of application  
and calculate hours, minutes, and seconds
elapsed. */
   seconds = (int)difftime ( time ( (time_t *)0 ),  
start_time ) ;
minutes = seconds / 60 ;
seconds %= 60 ;
hours = minutes / 60 ;
minutes %= 60 ;
   /* Print out time report. */
   printf ( "Application active for %d hours, %d minutes, "
"%d seconds.", hours, minutes, seconds ) ;
   return ( 0 ) ;
}

Record and Playback

The following example shows how record and playback might work together in a regression test.

The two functions record and play implement a simple mechanism for recording and later playing back keystrokes in a Panther application. The keystrokes are recorded to and played back from a file. The interval in seconds between keystrokes is also saved so that the playback function can pause to simulate real user behavior.

The following lines can be included in the main function to allow for conditional record and playback, assuming that the first parameter passed to the program was an optional indicator for record or playback:

if ( argc > 1 )
{
switch ( argv[ 1 ][ 0 ] )
{
case 'r' :
case 'R' :
sm_install( RECORD_FUNC, &record_struct,(int *)0);
break ;
case 'p' :
case 'P' :
sm_install ( PLAY_FUNC, &play_struct, (int *)0 ) ;
break ;
}
}

It is preferable for the main function to initialize the variable r_time rather than counting on this record/playback system to do it. Used as written, the interval before the very first key that the user types is not accurately recorded, and hence not accurately played back.

/* Include Files */
#include "smdefs.h"    /* screen manager Header Files */ 
static int intbuf[2] ; /* Buffer for read/write of
* keystroke data */
static FILE *fp ;        /*File pointer for keystroke file */
static time_t r_time ;   /*Time first character was gotten */
static time_t c_time ;   /* Current time;  
* interval=difftime(c_time, r_time)
                          */
static char key_file[ ]  /* Name of keystroke file */ 
= "recplay.key" ;

int
record ( key )
int key ; /* Key to be recorded */
{
/* If the file has not been opened, open it and
initialize r_time */
if ( ! fp )
{
/* Set the initial time. */
r_time = time ( (time_t *)0 ) ;

/* Open file */
fp = fopen ( key_file, "w" ) ;

/* Turn on record/playback system */
sm_keyfilter ( 1 ) ;
}

/* Get the current time */
c_time = time ( (time_t *)0 ) ;

/* Store the key to record in the data buffer */
intbuf[ 0 ] = key ;

/* Store the time interval in the data buffer */
intbuf[ 1 ] = floor ( difftime ( c_time, r_time )
+ 0.5 ) ;

/* Now write the data buffer to the keystroke file */
if ( ( ! fp ) ||
( fwrite ( (char *) intbuf, sizeof ( int ),
2, fp ) != 2 ) )
{
/* Write failed. Close everything down.... */
fclose ( fp ) ;
fp = NULL ;
intbuf[ 0 ] = 0 ;
sm_keyfilter ( 0 ) ;
sm_ferr_reset ( "Recording Terminated..." ) ;
}

return ( 0 ) ;
}
  
int
play ( )
{
/* If the file has not been opened, open it and
initialize r_time */
if ( ! fp )
{
r_time = time ( (time_t *)0 ) ;
fp = fopen ( key_file , "r" ) ;
sm_keyfilter ( 1 ) ;
}

/* Now read the keystroke file, one keystroke into
the data buffer */
if ( ( ! fp ) ||
( fread ( (char *) intbuf, sizeof ( int ),
2, fp ) != 2 ) )
{
/* Read failed. Close everything down.... */
fclose ( fp ) ;
fp = NULL ;
intbuf[ 0 ] = 0 ;
sm_keyfilter ( 0 ) ;
sm_ferr_reset ( "Playback Terminated...." ) ;
return ( 0 ) ;
}

/* Get the current time */
c_time = time ( (time_t *)0 ) ;

/* Decrement interval from data buffer by measured
interval */
intbuf[ 1 ] -= floor ( difftime ( c_time, r_time )
+ 0.5 ) ;

/* Sleep some more if we should. */
if ( intbuf[ 1 ] > 0 )
{
sm_flush ( ) ;
sleep ( intbuf[ 1 ] ) ;
}

/* Return the key to sm_getkey for processing */
return ( intbuf[ 0 ] ) ;
}

Control

The following example shows two closely related functions that you can include on a control function list. The mark_low function marks all widgets on the current screen with numeric values less than zero with an attribute change. The function mark_high marks all widgets on the current screen with numeric values higher than 1000. The same functionality is duplicated as the prototyped function mark_flds (see page 44-52).

Note that both mark_low and mark_high call the static function mark_flds which actually does the work. This may seem like unnecessary indirection, but it means that the control strings used are very simple, as shown here:

^mark_low
^mark_high
/* Include Files */
#include "smdefs.h" /* screen manager Header File */
#include "smglobs.h" /* screen manager Globals */
/* Macro Definitions... */
/* Attributes used to mark fields */
#define MARK_ATTR REVERSE | HILIGHT | BLINK
#define MARK_GT 1 /* Indicates "Greater Than" */
#define MARK_LT -1 /* Indicates "Less Than " */
static int mark_flds ( ) ;
int
mark_low ( ctrl_str )
char *ctrl_str;/* control string text passed by Panther */
{
/* Mark all fields less than zero */
return ( mark_flds ( 0, MARK_LT ) ) ;
}

int
mark_high ( ctrl_str )
char *ctrl_str;/* control string text passed by Panther*/
{
/* Mark all fields greater than one thousand */
return ( mark_flds ( 1000, MARK_GT ) ) ;
}

static int
mark_flds ( bound, operator )
int bound ; /* Boundary on fields to mark */
int operator ; /* Operator, MARK_GT or MARK_LT */
{
int fld_num ; /* Field Number */
int num_of_flds ; /* Number of Fields */

/* Determine number of fields */
num_of_flds = sm_inquire ( SC_NFLDS ) ;

/* Cycle through all the fields on the screen */
for ( fld_num = 1 ; fld_num <= num_of_flds ; fld_num++ )
{
/* Depending on the operator... */
switch ( operator )
{
case MARK_GT:
/* Mark fields that are
greater than the
given bound. */
if ( sm_dblval ( fld_num )
> ( double ) bound )
{
sm_chg_attr ( fld_num,
MARK_ATTR ) ;
}

break;

case MARK_LT:
/* Mark fields that are less
than the given bound */
if ( sm_dblval ( fld_num )
< ( double ) bound )
{
sm_chg_attr ( fld_num,
MARK_ATTR ) ;
}
break;
}
}
return ( 0 ) ;
}

The next example shows how a number of entries in a control function list might map to the same function, report, which uses the identifying string as an implied first argument. Significant argument processing is done in this example.

  1. If there is nothing on the control string following the name, the report is printed in a popup message window. For example, the following control string will generate a report about the current widget and display the report in a popup message window:
    ^rep_field
  2. If the arguments start with an exclamation point (!), the rest of the control string is taken to be an operating system command. In this case, a temporary file with the report will be created, and the file name will be appended to the operating system command. However, if the operating system command has a tilde (~) in it, the tilde will be replaced with the name of the file before the command is invoked. In any event the file is deleted after the command is invoked. Two example control strings that would cause a screen report to be printed on a UNIX system are shown below:
    ^rep_screen !lp -c -s
    ^rep_screen !lp -c ~ > /dev/null 2>&1
  3. If the arguments start with a vertical bar (|), the rest of the control string is taken to be an operating system command. In this case, however, the report will be created as the standard input of the specified command. Many operating systems call this piping. The example shown here will cause a window stack report to piped through the UNIX command tail and printed, so that only 20 lines of output will be printed:
    ^rep_wstack |tail -20 | lp -c -s
  4. If the arguments do not start with a vertical bar or with an exclamation point, the assumption is that it is a file that is named. The file will be created if it does not exist, or appended to if it does exist. The following example will append a display terminal report to the file report.fil:
    ^rep_term report.fil

This function installation is preceded with the following definitions and declarations, commonly found in funclist.c:

extern int report ( ) ; 
struct fnc_data report_funcs[] = {
SM_OLDFNC( "rep_field", report ),
SM_OLDFNC( "rep_screen", report ),
SM_OLDFNC( "rep_wstack", report ),
SM_OLDFNC( "rep_term", report )
} ;
int report_count = sizeof ( report_funcs ) 
/ sizeof ( struct fnc_data ) ;

The actual installation of the function is done with the following call to sm_install, usually found in sm_do_uinstalls, defined in funclist.c:

sm_install ( CONTROL_FUNC, report_funcs, &report_count ) ;

Note that the function list has four function entries with different names, all of which refer to the same function pointer. In the case of CONTROL_FUNC functions, the entire control string is passed to the called function in a string, the name used to invoke the function can—and in this case does—serve as an implied argument.

/* Include Files */
#include "smdefs.h"    /* screen manager Header File */ 
#include "smglobs.h" /* screen manager Globals */
int 
report ( report_type )
char *report_type ; /* Text of invoking control
string -- later truncated to the
name of the desired report */
{ 
char *report_out; /* Report output designation */
char *fn = NULL; /* Name of output file */
char *ptr, *ptr1; /* Character pointers */
char msg_buf[ 128 ];/* Message buffer */
FILE *fp ; /* File pointer for output */
int size ; /* Size of output file */
int cur_no ; /* Current field number */
int select ; /* Current window stack index */
   /* Set report output designator to control string 
* arguments
*/
for ( report_out = report_type ;
*report_out && ( ! isspace ( UNSIGN(*report_out) ) ) ;
report_out++ ) ;
   /* If control string has arguments.... */ 
if ( *report_out )
{
/* Truncate the report type with a terminator */
*report_out = '';
      /* Gobble up unnecessary white space */ 
for ( report_out++ ;
*report_out &&
( isspace ( *report_out ) ) ;
report_out++ ) ;
      /* Based on what output type we designated: */ 
switch ( *report_out )
{
case '!' :
/* OS command. Open temp file */
fn = tempnam ( NULL, "rprt" ) ;
fp = fopen ( fn, "w" ) ;
break ;

case '|' :
/* Pipe. Open the pipe */
fp = popen ( report_out + 1,
"w" ) ;
break ;

default :
/* Other. Open the file */
fp = fopen ( report_out, "a+" ) ;
break ;
}
      /* If we could not open the file, show error */ 
if ( ! fp )
{
sprintf ( msg_buf,
"Cannot open stream for %s.",
report_out ) ;
sm_ferr_reset ( msg_buf ) ;
return ( -1 ) ;
}
}
   /* If no report output specified, open temp file for 
storing message window stuff. */
else
{
fn = tempnam ( NULL, "rprt" ) ;
fp = fopen ( fn, "w+" ) ;
report_out = "" ;
}
   /* Now, based on the report_type, which is the name 
with which the function was invoked, create
the reports. Note that all newlines are
preceded with spaces, this is so that in the
case of the message windows we can replace
all space-newlines with %N, the newline
indicator for Panther windows. */
if ( ! strcmp ( report_type, "rep_field" ) )
   { 
/* Output a field report */
fprintf ( fp, " Field Report: " ) ;
      /* Field Identifier and contents */ 
fprintf ( fp, "FIELD: %d (%s[%d]) = %s ",
cur_no = sm_getcurno ( ),
sm_name ( cur_no ),
sm_occur_no ( ),
sm_fptr ( cur_no ) ) ;
      /* Field sizes */ 
fprintf ( fp, "LENGTH: onscreen: %d "
"Max: %d ",
size = sm_finquire ( cur_no, FD_LENG ),
sm_finquire ( cur_no, FD_SHLENG )
+ size ) ;
      fprintf ( fp, "# OCCURRENCES: onscreen: %d " 
"Max: %d ",
sm_finquire ( cur_no, FD_ASIZE ),
sm_max_occur ( cur_no ) ) ;
}
else if ( ! strcmp ( report_type, "rep_screen" ) )
{
/* Output screen report */
fprintf ( fp, " Screen Report: " ) ;

/* Screen Name */
fprintf ( fp, "SCREEN: %s ",
sm_pinquire ( SP_NAME ) ) ;

/* How much of screen is visible */
fprintf ( fp, "%% VISIBLE IN VIEWPORT: %d ",
100 *
( sm_inquire ( SC_VNLINE ) *
sm_inquire ( SC_VNCOLM ) ) /
( sm_inquire ( SC_NCOLM ) *
sm_inquire ( SC_NLINE ) ) ) ;
}
else if ( ! strcmp ( report_type, "rep_wstack" ) )
{
/* Output Window stack report */
fprintf ( fp, " Window Stack Report: " ) ;

/* Cycle through all the windows. */
for ( select = 0 ;
sm_wselect ( select ) == select ;
select++ )
{
/* Window number... */
fprintf ( fp, " Window %d: ",
select ) ;

/* Screen name */
fprintf ( fp, "screen: %s ",
sm_pinquire ( SP_NAME ) ) ;

/* Number of fields and groups */
fprintf ( fp, "# of Fields: %d "
"# of Groups: %d ",
sm_inquire ( SC_NFLDS ),
sm_inquire ( SC_NGRPS ) ) ;

sm_wdeselect ( ) ;
}
sm_wdeselect ( ) ;
}
else if ( ! strcmp ( report_type, "rep_term" ) )
{
/* Output display terminal report */
fprintf ( fp, " Terminal Report: " ) ;

/* Terminal Type */
fprintf ( fp, "TERM TYPE: %s ",
sm_pinquire ( P_TERM ) ) ;

/* Display mode */
if ( sm_inquire ( I_NODISP ) )
fprintf ( fp, "DISPLAY OFF " ) ;
else
fprintf ( fp, "DISPLAY ON " ) ;

/* Input mode */
if ( sm_inquire ( I_INSMODE ) )
fprintf ( fp, "INSERT MODE " ) ;
else
fprintf ( fp, "TYPEOVER MODE " ) ;

/* Block mode */
if ( sm_inquire ( I_BLKFLGS ) )
fprintf ( fp, "BLOCK MODE " ) ;

/* Physical display size */
fprintf ( fp, "DISPLAY SIZE: %d x %d ",
sm_inquire ( I_MXLINES ),
sm_inquire ( I_MXCOLMS ) ) ;
}
   else 
{
/* Unrecognized report type */
sprintf ( msg_buf, "Illegal report type %s",
report_type ) ;
sm_ferr_reset ( msg_buf ) ;
fprintf ( fp, "%s ", msg_buf ) ;
return ( -3 ) ;
}
   /* Once again, based on the type output... */ 
switch ( *report_out )
{
case '|' :
/* It was a pipe, so close it. */
pclose ( fp ) ;
sm_ferr_reset ( "Pipe successful" ) ;
break ;
   case '!' : 
/* It was an O/S command. Close file... */
fclose ( fp ) ;
      /* Gobble up the exclamation point */ 
report_out++;
      /* Look for tildes */ 
if ( ptr = strchr ( report_out, '~' ) )
{
/* Found the tilde. Substitute the
file name for it. */
*ptr = '';
sprintf ( msg_buf, "%s%s%s",
report_out, fn, ptr+1 ) ;
      } 
else
{
/* No tilde. Append file name to
O/S command. */
sprintf ( msg_buf, "%s %s",
report_out, fn ) ;
}
      /* Do the command. */ 
system ( msg_buf ) ;
      /* Delete temp file and free its name. */ 
remove ( fn ) ;
free ( fn ) ;
sm_ferr_reset ( "Command Invoked" ) ;
break ;
   case '': 
/* Message window. Get size of file... */
size = ftell ( fp ) ;
      /* Allocate memory for it. */ 
ptr = malloc ( size + 1 ) ;
      /* Rewind the file */ 
fseek ( fp, SEEK_SET, 0 ) ;
      /* Read it into the malloced buffer. */ 
fread ( ptr, sizeof ( char ), size, fp ) ;
      /* Close and delete file, free file name */ 
fclose ( fp ) ;
remove ( fn ) ;
free ( fn ) ;
      /* null terminate memory buffer of report */ 
ptr[size] = '';
      /* Replace all space-newlines with %N */ 
for ( ptr1 = ptr ;
ptr1 = strchr ( ptr1, '' ) ;
ptr1++ )
{
ptr1[-1]='%';
ptr1[0]='N';
}
      /* Pop up the message window */ 
sm_message_box
( ptr, 0, SM_MB_OK|SM_MB_ICONNONE, 0 ) ;
      /* Free up the malloced buffer. */ 
free ( ptr ) ;
break ;
   default : 
/* File appended, just close it. */
fclose ( fp ) ;
sm_ferr_reset ( "File appended" ) ;
break ;
}
return ( 0 ) ;
}

Status Line

The following example shows how to write a status line function. It is called whenever the logical status line is about to be flushed to the physical display, and ensures that the status line is always printed highlighted and in uppercase.

This function is to be installed as a status line function. The following declarations and definitions, generally found in funclist.c or in the main routine source module prepare this routine for installation:

/* Include Files */ 
#include "smdefs.h" /* screen manager Header File */
#include "smglobs.h" /* screen manager Globals */
int 
statln ( )
{
int n_columns ; /* Physical display width */
char * stat_text ; /* Status line text */
unsigned short * stat_attr;/* Status line attributes */
int i ; /* Loop counter */
int c; /* Upper case stat text char */
	/* Determine width of display */ 
n_columns = sm_inquire ( I_MXCOLMS ) ;
   /* Allocate memory for local buffers */ 
stat_text = malloc ( n_columns + 1 ) ;
stat_attr = (short *)calloc ( n_columns,
sizeof ( short ) ) ;
   /* Copy status text and attributes into buffers */ 
strcpy ( stat_text , sm_pinquire ( SP_STATLINE ) ) ;
memcpy ( ( char * ) stat_attr ,
sm_pinquire ( SP_STATATTR ) ,
n_columns * sizeof ( short ) ) ;
    /* Loop through every character on the status line */ 
for ( i = 0 ; i < n_columns ; i++ )
{
/* Set character to upper case */
/* Note UNSIGN is defined in smmachs.h to
remove sign extension */
c = stat_text [i];
if ( islower (UNSIGN(c)) )
c = toupper ( UNSIGN(stat_text[ i ]) ) ;
stat_text[ i ] = c ;
      /* Add hilight attribute */ 
stat_attr[ i ] |= HILIGHT ;
}
   /* copy local buffer into Panther internal buffers */ 
sm_pset ( SP_STATLINE , stat_text ) ;
sm_pset ( SP_STATATTR , ( char * ) stat_attr ) ;
   /* Free memory */ 
free ( stat_text ) ;
free ( stat_attr ) ;
    return ( 0 ) ;
}