Application Development |
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.
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 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.
Demand Functions
Automatic Functions
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:
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.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
.
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:
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:
SM_INTFNC
specifies that the function dereferences arguments supplied from JPL and returns an integer value.
The first value of a If the function type allows installation of only one function, supply 0.
Strings and integers are the only two data types 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.
The second value of a You install functions through the library function sm_install. For example, given the earlier This function takes three arguments:
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.
The name of the The address of a variable that contains the number of functions included in funcs. If funcs is an array of For example, this statement gets the number of functions installed in the Function Name
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.
Function Address
SM_*FNC
macro is the address of the function—that is, its C identifier.
Installing Functions
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 ) ;
func_type
funcs
fnc_data
structure that includes the functions to install.
num_funcs
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.
fnc_dat
a 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.
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:
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.
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.
All screen functions receive two arguments in this order:
The second parameter can have one or more of the following flags set:
The function was called on screen entry.
Equivalent:
The function was called on screen exit.
Equivalent:
The function was called for one of these reasons:
K_ENTRY
if(param2 & K_ENTRY)
K_EXIT
if (param2 & K_EXIT)
K_EXPOSE
K_EXIT
and K_EXPOSE
are set.
K_ENTRY
and K_EXPOSE
are set.
Equivalent:
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 A "normal" call to sm_close_window caused the screen to close.
Equivalent:
The screen closed because another form is displayed or because sm_resetcrt is called.
Equivalent:
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.
You can install only one function as the automatic screen function. The following statement, typically found in The following line of code, typically found in the function 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 The following line of code, typically found in the function if (param2 & K_EXPOSE)
K_KEYS
K_NORMAL
or K_OTHER
.
K_NORMAL
if ((param2 & K_KEYS) == K_NORMAL)
K_OTHER
if ((param2 & K_KEYS) == K_OTHER)
Screen Function Returns
Installation of an Automatic Screen Function
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)
;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
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 ) ;
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 properties. For an example, see page 44-65.
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.
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.
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 functions are called under the following conditions:
TAB
or NL
. Widget functions are called for validation only after the field's contents pass all other validations for the field.
BACKTAB
, arrow keys, and mouse clicks outside the field trigger field validation only if the setup variable IN_VALID is set to OK_VALID
. The default setting is OK_NOVALID
XMIT
is pressed. By default, this logical key is mapped to the function sm_s_val
, which validates all fields on the screen. The setup variable XMIT_LAST can also be set so screen validation occurs when TAB
and/or NL are pressed in a screen's last field.
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.
All field functions receive four arguments in this order:
Field Function Arguments
The last parameter can have one or more flags set. The following sections describe these flags:
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: 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 Equivalent: The field function was called on field entry.
Equivalent: The field function was called on field exit. Note that if neither Equivalent: The field function was called because a window overlying this field's screen opened or closed:
Equivalent: The field is an extended selection list box.
For extended selection list boxes, the field is the last item in the list box.
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.
A "normal" key caused the cursor to enter or exit the field in question. For field entry, "normal" keys are Equivalent: The Equivalent: An arrow key caused the cursor to enter or exit the field.
Equivalent: The field is being validated as part of screen validation.
Equivalent: The field is being validated directly from the application with sm_fval or sm_validate.
Equivalent: A key other than Equivalent: The 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.
You can install only one function as the automatic field function. The following statement, usually found in The following line of code, usually found in the function You can install multiple functions as demand field functions. The following statements, usually found in The following line of code, usually found in the function VALIDED
if(param4 & VALIDED)
MDT
PV_NO
, or clear all fields on the screen by calling sm_cl_all_mdts
.
if(param4 & MDT)
K_ENTRY
if(param4 & K_ENTRY)
K_EXIT
K_ENTRY
nor K_EXIT
are set, the field is undergoing validation.
if(param4 & K_EXIT)
K_EXPOSE
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.
if(param4 & K_EXPOSE)
K_EXTEND
K_EXTEND_LAST
K_KEYS
K_NORMAL
NL
, TAB
, HOME
, and EMOH
. For field exit, only TAB
and NL
are considered "normal."
if((param4 & K_KEYS)==K_NORMAL)
K_BACKTAB
BACKTAB
key caused the cursor to enter or exit the field.
if((param4 & K_KEYS)==K_BACKTAB)
K_ARROW
if((param4 & K_KEYS)==K_ARROW)
K_SVAL
if((param4 & K_KEYS)==K_SVAL)
K_USER
if((param4 & K_KEYS)==K_USER))
K_OTHER
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."
if((param4 & K_KEYS)==K_OTHER)
K_INSDEL
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
Installation of an Automatic Field Function
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 ) ;
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
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 ) ;
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 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 All grid functions receive three arguments in this order:
EXPHIDE_OPTION
is set to ON_EXPHIDE
.
EXPHIDE_OPTION
is set to ON_EXPHIDE
. On exit, the grid row exit function is called before the grid exit function.
Grid Function Arguments
The last parameter can have one or more flags set. The following sections describe these flags:
The function was called on grid or row entry.
Equivalent: The function was called on grid or row exit. Note that if neither Equivalent: The function was called because a window overlying this grid's screen opened or closed:
Equivalent: 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.
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 Equivalent: The Equivalent: An arrow key caused the cursor to enter or exit the grid or grid row.
Equivalent: The grid is being validated as part of screen validation.
Equivalent: The grid is being validated directly from the application with Equivalent: A key other than Equivalent: The Grid functions return meaningful values only if called as the grid's validation function— You can install multiple functions as demand grid functions. The following statements, typically found in The following line of code, typically found in the function K_ENTRY
if (param3 & K_ENTRY)
K_EXIT
K_ENTRY
nor K_EXIT
are set, the grid is undergoing validation.
if (param3 & K_EXIT)
K_EXPOSE
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.
if(param3 & K_EXPOSE)
K_KEYS
K_NORMAL
NL
, TAB
, HOME
, and EMOH
. For grid exit, only TAB
and NL
are considered "normal."
if((param3 & K_KEYS)==K_NORMAL)
K_BACKTAB
BACKTAB
key caused the cursor to enter or exit the grid or grid row.
if((param3 & K_KEYS)==K_BACKTAB)
K_ARROW
if((param3 & K_KEYS)==K_ARROW)
K_SVAL
if((param3 & K_KEYS)==K_SVAL)
K_USER
sm_fval
.
if((param3 & K_KEYS)==K_USER))
K_OTHER
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."
if((param3 & K_KEYS)==K_OTHER)
K_INSDEL
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
0
if successful, non-zero if not.
Installation of Demand Grid Functions
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) ;
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.
All tab card functions receive two arguments in this order:
Tab Control Function Arguments
The last parameter can have one or more flags set. The following sections describe these flags:
The function was called on tab card entry.
Equivalent: The function was called on tab card exit.
Equivalent: 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: For the card hide event, all three flags are clear.
K_ENTRY
if(param2 & K_ENTRY)
K_EXIT
if(param2 & K_EXIT)
K_EXPOSE
if(param2 & K_EXPOSE)
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:
TAB
or selecting from an autotab group. BACKTAB
, arrow keys and mouse clicks outside the group also cause validation, unless the setup variable IN_VALID
is changed from its default setting to OK_NOVALID
.
XMIT
.
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.
All group functions receive two arguments:
Group Function 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 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.
You can install only one function as the automatic group function. The following statement, usually found in funclist.c, includes the automatic group function The following line of code, usually found in the function You can install multiple functions as demand group functions. The following statements, usually found in The following line of code, usually found in the function VALIDED
and MDT
bits to avoid redundant processing.
Group Function Returns
Installation of an 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 ) ;
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
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 ) ;
sm_do_uinstalls
in funclist.c
, installs the functions in gfuncs
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.
A client connection authentication (DATAFUNC
) function requires five arguments:
You can install multiple Install a single The following line of code, typically found in the function You cam install multiple The following line of code, typically found in the function 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
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.
DATAFUNC
function as follows:
struct fnc_data authpre =
SM_STRFNC ("user_datafunc", user_datafunc);
sm_do_uinstalls
in funclist.c
, installs user_datafunc
as the default client authentication connection function:
sm_install ( TP_INITDATA_FUNC, authpre, (int *)0);
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);
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.
All POSTFUNC
functions require six arguments:
DATAFUNC
function.
You can install multiple Install a single Define the function in a The following line of code, typically found in the function You can install multiple The following line of code, typically found in the function For further information on client authentication, refer to Client Post-Connection Returns
void (none)
Installation
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.
POSTFUNC
function as follows:
fnc_data
structure.
struct fnc_data authpost =
SM_STRFNC ("user_postfunc", user_postfunc);
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 );
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);
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);
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.
The help function gets a single string that contains the help context identifier.
PI_ERR_NONE (success) or
PI_ERR_NO_MORE (failure).
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 functions get one integer argument that tells why the function was called:
No keyboard activity occurred for the amount of time specified by this function's timeout interval.
Keyboard input was received during execution of the timeout function.
A timeout function should return a code that indicates whether Panther should keep calling the timeout function after each lapse of the timeout interval:
Keep calling the user function each timeout the interval elapses.
Do not call the timeout function again until keyboard input is received.
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 functions get one integer argument that tells why the function was called:
The timer's interval has expired.
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.
A timer function should return a code that indicates whether Panther should keep calling the function after each lapse of the timer interval:
Call the timer function extra times if timer expiration calls were missed.
Do not call the timer function again until the next timer expiration.
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.
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.
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.
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.
The error function gets three arguments in this order:
smerror.h
; for user-defined messages, as defined in user-created message header files. If the calling function passes a text string to display, this argument is -1.
1
specifies yes, a value of 0
specifies no. The value will be 1
for messages displayed by JPL calls to msg qui_msg and msg quiet and the message functions sm_fquiet_err, sm_fqui_msg, sm_quiet_err
and sm_qui_msg
.
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.
You can install only one error function. The following statement, usually found in The following line of code, usually found in the function Error Function Returns
Installation
funclist.c
, includes error function myerr
in the fnc_data
structure err_struct
.
struct fnc_data err_struct = SM_OLDFNC( 0, myerr ) ;
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 uninstalls its insert toggle function; the insert toggle function that you install can call Panther's insert toggle function directly.
This function gets one integer argument, which specifies the new mode:
1
—Insert mode
The insert toggle function should return 0.
You can install only one insert toggle function. The following statement, usually found in The following line of code, usually found in the function Returns
Installation
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 ) ;
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.
The check digit function gets these arguments:
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.
You can install only one check digit function. The following statement, usually found in The following line of code, usually found in the function Returns
Installation
funclist.c
, includes the check digit function ckdigit
in the fnc_data
structure ckdgt_struct
.
\Qstruct fnc_data ckdgt_struct = SM_OLDFNC( 0, ckdigit ) ;
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.
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.
Both the initialization and reset functions should return 0.
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:
sm_getkey
calls the record function just before it returns a translated key value to the application.
sm_getkey
also calls the playback function in place of a read from the keyboard.
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 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.
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.
The record function should return 0. The playback function should return the logical key previously recorded.
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 The following lines of code, usually found in the function sm_getkey
for more information.
Arguments
Returns
Installation
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 ) ;
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.
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.
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.
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.
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.
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.
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.
The video processing function receives two arguments:
smvideo.h
and outlined in Table 44-2.
NULL
.
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.
You can install only one video processing function. The following statement, usually found in The following line of code, usually found in the function Returns
Installation
funclist.c
, includes the video processing function video in the fnc_data
structure video_struct
.
struct fnc_data video_struct = SM_OLDFNC( 0, video ) ;
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-42 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.
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
.
Table 44-3 summarizes possible return codes for transaction manager functions:
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.
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.
This section has two sample functions:
mark_flds
gets a range of values and highlights all widgets whose data is within that range.
For example, this control string highlights all values on the screen between zero and 500:
The next control string highlights all values on the screen that are greater than 1000 or less than -300:
The following code comprises the entire ^mark_flds (0, 500)
^mark_flds (1000, -300)
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:
field
, screen
, wstack
, or term
.
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:
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:
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 The following code comprises the entire report function.
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 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 The This section has two sample functions:
^report ("screen","!lp -c -s")
^report ("screen", "!date | cat - ~ | lp -s")
^report ("wstack", "| tail | lp -s")
report.fil
:
^report("term", "report.fil")
/* 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
P_USER
pointer, a general purpose pointer that you can manipulate, which is associated with an open screen.
ON_EXPHIDE
, so Panther calls functions on screen overlay and reexposure.
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
auto_ffunc
puts general information about the current widget on the status line.
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.
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
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, The group function Note that preexisting keychange functions should be stacked by For a more extended example of keychange functions, see page 44-79.
The following help driver is supplied with Panther; it invokes context-sensitive help from the screen editor.
The following key change function intercepts each occurrence of the user entering an exclamation point or striking the This application keychange function causes sm_getkey to intercept two keys, the exclamation point and the logical Note that if the user escapes to the shell or does not want to 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 The following error function writes all user messages to a log file.
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 This routine assumes that cursor position display is not in use. You may also need a 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.
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 The two functions below, Example 1
/* 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
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
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
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
uninstalls keychg
.
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.
/* 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.
/*** 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 requestRETURNS
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
EXIT
key.
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
.
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
.
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
#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
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.
STAT_FUNC
function for this, as Panther overwrites the status line with messages, thus destroying the INS/OVR
message.
/* 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
sm_initcrt
is called.
uinit
and ureset
, are to be installed as the initialization and reset functions respectively.
uinit
is used to initialize the automatic variable start_time
. Then uinit
asks the user to enter a terminal type, and passes the string back to sm_initcrt
for processing. Finally, uinit
establishes error handling that causes the application to terminate gracefully on a number of software signals.
Note that 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:
It is preferable for the main function to initialize the variable The following example shows two closely related functions that you can include on a control function list. The Note that both The next example shows how a number of entries in a control function list might map to the same function, 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
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 ;
}
}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
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).
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 ) ;
}report
, which uses the identifying string as an implied first argument. Significant argument processing is done in this example.
report
creates a report about the state of the current widget, screen, window stack, or display. The report can be appended to a file, passed as an argument to an operating system command, piped to an operating system command, or displayed in a message window.
^rep_field
^rep_screen !lp -c -s
^rep_screen !lp -c ~ > /dev/null 2>&1
^rep_wstack |tail -20 | lp -c -s
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 ) ;
}
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 ) ;
}