This chapter describes four complete examples of applications using C API actions. The files containing these applications are in the directory $EMPRESSPATH/rdbms/gui/examples. These applications are:
| Application Name | Description |
| SimpleApp | A simple application using mx routines to access a database. It accesses text fields in the main window. |
| Images_c_api | Illustrates the use of image fields, toggle buttons, scales, and multiple windows. It uses mr routines. |
| ListApp | Uses embedded SQL. Illustrates the use of multilist, and multiple modules. |
| ExamResults | Illustrates the use of a user object as a drawing area for displaying a bar chart. Uses embedded SQL. |
| FlatFillApp | Reads a flat file and displays the data in a multilist in Empress GUI Builder. |
In the $EMPRESSPATH/rdbms/gui/examples directory, the file mkexdb is a script file which creates a GUI database and imports the example applications and data tables.
Run the script as follows:
$EMPRESSPATH/rdbms/gui/examples/mkexdb
It will create a database called examp_db in your current directory.
In the $EMPRESSPATH/rdbms/gui/examples directory, the file mkexgui is a script file which compiles the C files to executable. It contains the following statements:
: '(c) Copyright Empress Software Inc. 1983, 2006'
case "$EMPRESSPATH" in
"")
EMPRESSPATH='cd ../..; pwd'
;;
esac
EXDIR=${EMPRESSPATH}/rdbms/gui/examples
. $EMPRESSPATH/rdbms/util_bin/banner
$$EMPRESSPATH/bin/empesql ${EXDIR}/ex3_actn.c ex3_actp.c
$EMPRESSPATH/bin/empesql ${EXDIR}/ex4_actn.c ex4_actp.c
$EMPRESSPATH/bin/empguicc -ESQL -o ex_gui ${EXDIR}/ex1_actn.c \
${EXDIR}/ex2_actn.c ex3_actp.c ex4_actp.c ${EXDIR}/ex5_actn.c
${EXDIR}/ex_actab.c
rm -f ex3_actp.c ex4_actp.c ex1_actn.o ex2_actn.o ex3_actp.o ex4_actp.o
\
ex5_actn.o ex_actab.o
Note that ex3_actn.c and ex3_actn.c have to be precompiled with the Empress SQL C Precompiler, and that the final linking requires the -ESQL flag to indicate that the embedded SQL libraries should be included.
Run the script to create an executable named ex_gui :
$EMPRESSPATH/rdbms/gui/examples/mkexguiRun your ex_gui executable, with the database and the application name as arguments. For example, to execute the first application, SimpleApp:
ex_gui examp_db SimpleApp
In the ex_actab.c file, the user_action_table array contains entries which specify the action table associated with each module. These action tables are declared as extern, since they are defined in other files.
#include <guicc.h>
extern action_tab_entry msgui_public_action_table[];
extern action_tab_entry SimpleApp_action_table[];
extern action_tab_entry Images_action_table[];
extern action_tab_entry ListMain_action_table[];
extern action_tab_entry ListSub_action_table[];
extern action_tab_entry EResult_action_table[];
extern action_tab_entry FlatFile_action_table[];
static action_tab_def user_action_table[]
=
{
/* Module name,
Action table */
{ "PUBLIC GENERAL MODULE",
msgui_public_action_table },
{ "SimpleApp",
SimpleApp_action_table },
{ "Images_c_api",
Images_action_table },
{ "ListMain",
ListMain_action_table },
{ "ListSub",
ListSub_action_table },
{ "ExamResults",
EResult_action_table },
{ "FlatFileApp",
FlatFile_action_table },
{ CHARNIL }
};
action_tab_def* msgui_user_action_table = user_action_table;
| File: | $EMPRESSPATH/rdbms/gui/examples/ex1_actn.c |
| Application Name: | SimpleApp |
| Module Name: | SimpleApp |
| Description: | Displays records from a table. Allows records to be selected, updated, deleted, inserted. |
| Database Interface: | mx Routines |
| Concepts Illustrated: | Initializing C API, Getting objects, Checking status code, Assigning values to fields, Getting values of fields, Setting focus to a field, Using dialogs. |
/*********************************************************************
* (c) Copyright Empress Software Inc. 1983, 2006
**********************************************************************/
#include <guicc.h>
/* for the GUI C API routines */
#include <mscc.h>
/* for the MX routines */
#define ACTION_TBL_NAME SimpleApp_action_table
static void check_field ();
static void clear_fields ();
static void delete ();
static void enter_action ();
static void exit_action ();
static void fetch ();
static void insert ();
static void select_rec ();
static void update ();
action_tab_entry ACTION_TBL_NAME[] =
{
{ "a_check", check_field
}, /* Each field exit action */
{ "a_clear", clear_fields },
/* 'Clear' pushbutton */
{ "a_delete", delete
}, /* 'Delete' pushbutton */
{ "a_enter", enter_action },
/* Module enter action */
{ "a_exit", exit_action
}, /* Module exit action */
{ "a_fetch", fetch
}, /* 'Next', 'Previous' buttons */
{ "a_insert", insert
}, /* 'Insert' pushbutton */
{ "a_select", select_rec
}, /* 'Select' pushbutton */
{ "a_update", update
}, /* 'Update' pushbutton */
{ CHARNIL }
};
The next few lines define a few constants and several global variables. Note that empgui_c_dbname is a global variable defined in Empress GUI Builder. The variables for the main window object and the field objects are made global so that any routine in the module can have access to them.
#define DB empgui_c_dbname /* Name
of current database */
#define TAB "employee"
/* Name of table */
#define NUM_ATTRS 4
/* Number of attributes in table */
static object mainwin;
/* MAINWIN window */
static object* p_objs;
/* pointer to fields */
static char message_str[128]; /* used for building error messages */
/*** Null terminated lists of attribute names and field names ***/
static char* attr_names[] =
{ "name", "phone", "position",
"date_hired", CHARNIL };
static char* field_names[] =
{ "name", "phone", "position",
"date_hired", CHARNIL };
The first routines in the program deal with error and warning conditions. When a C API function is called, it always returns a status code to indicate whether it completed successfully or not. Depending on the function, a failure may be serious or not. For example, if empgui_c_initialize() fails, it is an error condition, and the program cannot continue. On the other hand, if empgui_c_field_set() fails, it may be considered a warning condition, and the program can continue. Typically, status messages are displayed in a warning or error dialog box. The C API functions to create dialog boxes take many arguments to allow customization; some of the routines below simplify the creation of dialogs by providing default values for most of the arguments. Note that the error_dialog() routine below also calls empgui_c_module_exit() to terminate the module after displaying the error message.
/*** Error Checking Routines ***/
#define NOSTYLE (char*)0
#define NOHELP (char*)0
static void print_status (str, status)
/* if not success, */
char*
str;
/* print status to stderr */
gui_status status;
{
if (status != E_C_SUCCESS)
fprintf (stderr, "Error:
%s, Status code: %d\n", str,
status);
}
void info_dialog (title, message) /* display message in info
dialog */
char* title;
char* message;
{
print_status ( "Dialog
info",
empgui_c_dialog_info (NOSTYLE, title, message,
POSITION_CENTER ) );
}
void error_dialog (title, message) /* display message in error
dialog, */
char* title;
/* and terminate module */
char* message;
{
print_status ( "Dialog
error",
empgui_c_dialog_error (NOSTYLE, title, message,
POSITION_CENTER, NOHELP) );
print_status ( "Exit module", empgui_c_module_exit
() );
}
boolean warning_dialog (title, message) /* display message
in
warning dialog, */
char* title;
/* return true if user clicks on OK, */
char* message;
/* false if user clicks on Cancel */
{
char* selected;
print_status ( "Dialog
warning",
empgui_c_dialog_warning (NOSTYLE, title, message,
POSITION_CENTER, DEFAULT_BUTTON_1, "OK",
"Cancel", NOHELP, &selected) );
if (selected[0] == 'C')
return (false);
else
return (true);
}
void check_error (str, status) /* if not success, */
char*
str; /* display message
in error dialog */
gui_status status;
{
char* message_str;
if (status != E_C_SUCCESS)
{
print_status ("Status
message",
empgui_c_status_message (status, & message_str) );
error_dialog (str, message_str);
}
}
boolean check_warning (str, status) /* if not success, */
char*
str;
/* display message in warning
dialog */
gui_status status;
{
char* message_str;
char* selected;
if (status != E_C_SUCCESS)
{
print_status ("Status
message",
empgui_c_status_message (status, & message_str) );
return (warning_dialog
(str, message_str));
}
return (true);
}
The first routine that will be executed when the module is started is enter_action(). Hence, this routine needs to perform initialization of the C API and get all the objects that need to be accessed in this program. empgui_c_initialize() performs initialization, and gets the main window. empgui_c_objs_get_by_names() takes an array of field names and creates an array of objects. The routine subsequently opens the table in the database, and calls the select() routine to get records from the table.
/*** Initialization: Get all objects required, and open table in
database ***/
static void enter_action
(obj, param)
object
obj;
char*
param;
{
void select();
mxerret = 1; /* continue on mx error */
check_error (
"Initialize",
empgui_c_initialize
( & mainwin, (object*) 0, CHARNIL)
);
check_error (
"Get objects IDs",
empgui_c_objs_get_by_names
( mainwin, field_names, &
p_objs )
);
if (! mxopen (DB, TAB,"d"))
error_dialog ("Open
table failed", mrerrmsg());
select_rec (obj, "init");
}
The select() routine is executed when the user clicks on the Select button. It gets the contents of the fields and creates qualifications to select only records where the attributes match the patterns in the corresponding fields. empgui_c_props_get() is used to get the values of all the fields into an array of string pointers, which needs to be freed afterwards, using empgui_c_array_free(). The routine enter_action() also calls select(), passing it the string "init" as parameter. If that is the case, then qualifications are not needed.
/*** Get the values of the fields, and select records where
the attributes match the patterns in the corresponding
fields ***/
static void select_rec (obj, param)
object obj;
char*
param;
{
char** datas;
int i;
int num=0;
void fetch();
if ((param == CHARNIL) || (strcmp (param, "init")
!= 0))
{
mxgetend (); /* Terminate
previous retrieval */
check_error (
"Get objects values",
empgui_c_props_get ( p_objs, PROP_VALUE, &datas )
);
for (i=0 ; i<NUM_ATTRS;
i++) /* Create qualifications */
{
if (**(datas+i) != (char)0) /* field is not
empty */
{
if (mxqmch ("match", attr_names[i],
*(datas+i)) )
num++;
else
{
sprintf (message_str,
"Match qualification (mxqmch) failed for attribute %s",
attr_names[i]);
warning_dialog ("Qualification", message_str);
}
}
}
empgui_c_array_free
(datas);
for (i=num; i>1; i )
mxqand();
}
if ( mxgetbegin (TAB, CHARNIL ) ) /* Begin retrieval
*/
fetch (obj, "Next");
else
warning_dialog ("Select",
"Retrieval initialization
failed");
}
The fetch() routine gets the next or previous record, depending on param. It is called when the user clicks on the Next or Previous pushbuttons. The parameter property of these buttons have values Next and Previous respectively, and these are passed to the fetch() routine as param.
/*** Fetch the next or previous record, and inform user in case
of
failure ***/
static void fetch (obj, param)
object obj;
char
param;
{
int got_rec;
void display_attributes();
switch (*param)
{
case 'N':
got_rec = mxget();
break;
case 'P':
got_rec = mxprev();
break;
default:
sprintf (message_str,
"Invalid Parameter %s", param);
error_dialog ("Fetch", message_str);
}
if (got_rec != 1)
{
clear_fields (obj, param);
sprintf (message_str,
"Cannot Fetch %s Record", param);
warning_dialog ("Fetch",
message_str);
}
else
display_attributes ();
}
The display_attributes() routine reads the attribute values for the current record, and assigns these values in the corresponding fields. The attribute values are stored as an array of string pointers. empgui_c_props_set() takes the array of objects and the array of values to perform the assignment.
/*** Get values of attributes and display in corresponding fields ***/
static void display_attributes ()
{
char* values[NUM_ATTRS];
int i;
for (i=0; attr_names[i] != CHARNIL ; i++)
values[i] = mxgetvs
(attr_names[i]);
check_warning (
"Set objects values",
empgui_c_props_set ( p_objs, PROP_VALUE, values )
);
}
The clear_fields() routine simply assigns an empty string to each field, using empgui_c_field_set(). empgui_c_props_set could also be used, but it would require an array of pointers to empty strings for the values.
/*** Clear the contents of the fields ***/
static void clear_fields (obj, param)
object obj;
char*
param;
{
object* pobj;
for (pobj = p_objs ; *pobj != (object)0; pobj++)
{
check_warning (
"Set field value",
empgui_c_field_set ( * pobj, "")
);
}
}
The delete() routine is executed when the Delete button is clicked. It deletes the current record, clears the fields and pops up an info dialog.
/*** Delete the current record, and inform user of success or failure ***/
static void delete (obj, param)
object obj;
char*
param;
{
char* str;
/* dummy pointer */
if ( mxdel(TAB) )
{
clear_fields (obj, param);
info_dialog ("Delete",
"Record deleted");
}
else
warning_dialog ("Delete",
mrerrmsg());
}
The check_field() routine is executed when the user attempts to exit a field. It gets the contents of the current field (which is the first argument to the routine) using empgui_c_field_get(), and tries to assign the value to the corresponding attribute, which is specified by param, the second argument. If the value is not valid for the attribute, a warning dialog is popped up, and the input focus is set to the same field.
/*** Check that the content of the current field is valid for the
corresponding attribute
***/
static void check_field (obj, param)
object obj;
char*
param;
{
char* data;
check_error ( "Get field value",
empgui_c_field_get ( obj, & data ) );
if (! mxputvs ( param, data ))
{
sprintf ( message_str,
"Value '%s' not valid for
attribute %s",
data, param );
warning_dialog ("Field",
message_str);
check_error ( "Set field
focus",
empgui_c_field_set_focus ( obj ) );
}
}
The set_attrs() routine gets the values of all the fields using empgui_c_props_get(), and assigns them to the corresponding attributes. It returns an error message if any of the values are not valid.
/*** Assign the values of the fields to the corresponding attributes ***/
static char* set_attrs ()
{
char** datas;
int i;
check_warning (
"Get objects values",
empgui_c_props_get (
p_objs, PROP_VALUE, &datas )
);
for (i=0 ; i<NUM_ATTRS; i++)
{
if (! mxputvs (attr_names[i],
*(datas+i)) )
{
sprintf ( message_str,
"Value '%s' not valid for attribute %s",
*(datas+i), attr_names[i] );
empgui_c_array_free (datas);
return (message_str);
}
}
empgui_c_array_free (datas);
return (CHARNIL);
}
The insert() routine is called when the Insert button is clicked. It assigns the values of the fields to the attributes, and inserts a new record.
/*** Insert a new record using the values in the fields,
and inform user of success
or failure ***/
static void insert (obj, param)
object obj;
char*
param;
{
char* message;
if ((message = set_attrs()) != CHARNIL)
warning_dialog ("Insert",
message);
else
if ( mxadd(TAB) )
info_dialog ("Insert", "Record inserted");
else
warning_dialog ("Insert", mrerrmsg());
}
The update() routine is called when the Update button is clicked. It assigns the values of the fields to the attributes, and updates the current record.
/*** Update the current record using the values in the fields,
and inform user of success
or failure ***/
static void update (obj, param)
object obj;
char*
param;
{
char* message;
if ((message = set_attrs()) != CHARNIL)
warning_dialog ("Update",
message);
else
if ( mxput(TAB) )
info_dialog ("Update", "Record updated");
else
warning_dialog ("Update", mrerrmsg());
}
The exit_action() routine is called when the module is terminated. It is used for clean-up purposes.
/*** Cleanup: Terminate retrieval, and close table. ***/
static void exit_action (obj, param)
object obj;
char*
param;
{
mxgetend ( );
mxaclose ( );
print_status ( "Array free", empgui_c_array_free
( p_objs ) );
}
| File: | $EMPRESSPATH/rdbms/gui/examples/ex2_actn.c |
| Application Name: | Images_c_api |
| Module Name: | Images_c_api |
| Description: | Displays records from a table containing pictures. Allows records to be automatically retrieved and displayed with a user-controllable time interval. |
| Database Interface: | mr Routines |
| Concepts Illustrated: | Handling image bulk data, Getting & setting scale value, Accessing the menubar, Controlling sensitivity of objects, Hiding & showing a window. |
The first part of the program contains the action table for the module. The table has an entry for each C action for the module, and the corresponding C routine to call. Note that the last entry in the action table ends is null.
/***********************************************************************
* (c) Copyright Empress Software Inc. 1983, 2006
***********************************************************************/
#include <guicc.h> /* for the GUI C API
routines */
#include <mscc.h> /* for the MR
routines */
#define ACTION_TBL_NAME Images_action_table
static void next ();
static void prev ();
static void slide ();
static void show_win ();
static void enter_action ();
static void exit_action ();
action_tab_entry ACTION_TBL_NAME[] =
{
{ "next_pic", next
}, /* 'Next' pushbutton */
{ "prev_pic", prev
}, /* 'Previous' pushbutton */
{ "slide_mode", slide
}, /* 'Slide Mode' toggle */
{ "show_scale", show_win
}, /* 'Show Scale' toggle */
{ "select_pic", enter_action }, /* Module
enter action */
{ "cleanup", exit_action
}, /* Module exit action */
{ CHARNIL }
};
The following section defines several global variables for the various objects that need to be accessed in this program, as well as several mr descriptors.
#define TAB "pictures"
static object name_fld; /* name field
*/
static object categ_fld; /* category field
*/
static object image_fld; /* image field
*/
static object next_pbn; /* Next pushbutton
*/
static object prev_pbn; /* Prev pushbutton
*/
static object slide_tbn; /* slide mode
togglebutton */
static object view_cbn; /* View cascade
button */
static object show_tbn; /* show scale
togglebutton */
static object interval_scl; /* time interval scale */
static object time_win; /* time interval
window */
static addr timerid; /* id for timer mechanism */
static addr table;
/* table descriptor */
static addr record;
/* record descriptor */
static addr retrieval; /* retrieval
descriptor */
static addr a_name;
/* attribute descriptors */
static addr a_category;
static addr a_image;
For dialog and error checking routines, we can use those defined in the SimpleApp module. So, these routines are declared as extern here. In addition, the mr_error() routine handles errors generated by the mr routines.
/*** Dialog and Error checking routines ***/
extern void info_dialog ();
extern void error_dialog ();
extern boolean warning_dialog ();
extern void check_error ();
extern boolean check_warning ();
static void mr_error (str)
/* When an MR routine fails, */
char*
str;
/* display message in error dialog */
{
error_dialog (str, mrerrmsg());
}
The enter_action() routine is the first one to be executed when running the module. It performs initialization of the C API using empgui_c_initialize() and gets the main window, the menubar and other bins (such as the time_interval window and the view pull-down menu). Fields and buttons in the main window are obtained using empgui_c_objs_get_by_name_pair(). The scale in the time_interval window, the toggle button in the view menu and the view cascade button in the menubar are all obtained using calls to empgui_c_obj_get_by_name().
/*** Action executed when starting this module ***/
static void enter_action (obj, param)
object obj;
char*
param;
{
object mainwin;
object menubar;
object view_menu;
void next();
/* c_api initialization */
check_error (
"Initialize",
empgui_c_initialize
( & mainwin, & menubar,
"time_interval",
& time_win,
"view",
& view_menu, CHARNIL)
);
check_error (
"Get objects in mainwin",
empgui_c_objs_get_by_name_pair
( mainwin,
"name", &
name_fld,
"category", & categ_fld,
"image", & image_fld,
"Next", &
next_pbn,
"Previous", & prev_pbn,
"slide_mode", & slide_tbn,
CHARNIL)
);
check_error (
"Get scale object in
time window",
empgui_c_obj_get_by_name
( time_win, "interval", & interval_scl)
);
check_error (
"Get toggle button in
view menu",
empgui_c_obj_get_by_name
( view_menu, "show_scale", & show_tbn )
);
check_error (
"Get view button in
menubar",
empgui_c_obj_get_by_name
( menubar, "view", & view_cbn )
);
After obtaining all the objects, the next step is to make the view cascade button insensitive by calling empgui_c_sen_set(), and to set the initial scale value to 5 using empgui_c_scale_set(). Then the database initialization is performed - opening the pictures table, creating table, record, attribute descriptors, etc.
check_warning (
"Set View button insensitive",
empgui_c_sen_set ( view_cbn,
false )
);
check_warning (
"Set scale value",
empgui_c_scale_set (
interval_scl, 5 )
);
/* database routines */
if ((table = mrtopen (empgui_c_dbname, TAB,
'r')) == ADDRNIL)
{
mr_error ("Open Table");
}
record = mrmkrec (table);
a_name = mrngeta (table, "name");
a_category = mrngeta (table, "category");
a_image = mrngeta (table, "image");
if ((retrieval = mrtgtbegin (ADDRNIL, record,
ADDRNIL)) == ADDRNIL)
{
mr_error ("Select records");
}
next (obj, param);
}
The next() and prev() routines are called when activating the User-Define and Prev buttons respectively. They fetch the next or previous record, and call the display() routine. The latter checks the status of the fetch - if there are no more records, an information dialog is displayed. If the record was obtained, the values of the name and category attributes are retrieved and displayed in the respective fields using empgui_c_field_set(), and the data in the image attribute is displayed in the image field using empgui_c_image_set().
/*** Get attributes values from fetched record and copy to fields ***/
static void display (state, param)
int
state;
char*
param;
{
char title[32];
void clear_fields ();
sprintf (title, "%s Picture", param);
switch (state)
{
case 0: clear_fields
();
info_dialog (title, "No more pictures.");
break;
case 1: check_warning
( "set field value",
empgui_c_field_set ( name_fld,
mrgetvs (record, a_name) ) );
check_warning ( "set field value",
empgui_c_field_set ( categ_fld,
mrgetvs (record, a_category) ) );
check_warning ( "set image field value",
empgui_c_image_set ( image_fld,
mrgeti (record, a_image) ) );
break;
default: mr_error ("Fetch");
}
}
/*** Action for "Next" pushbutton: fetch the next record and display
it
***/
static void next (obj, param)
object obj;
char*
param;
{
display ( mrtget (retrieval), "Next" );
}
/*** Action for "Previous" pushbutton: fetch previous record and
display
it ***/
static void prev (obj, param)
object obj;
char*
param;
{
display ( mrtprev (retrieval), "Previous" );
}
To clear the contents of the text fields, an empty string is passed to empgui_c_field_set(). To clear the contents of an image field, an empty image must be passed to empgui_c_image_set(). Since the first 4 or 8 bytes (depending on sizeof(long) ) of the image bulk structure indicate the size (number of bytes) of the data, an empty image is simply (long)0.
/*** Set text fields and image field to null ***/
static void clear_fields ()
{
long null_image = 0;
check_warning ( "set field value",
empgui_c_field_set ( name_fld, "" ) );
check_warning ( "set field value",
empgui_c_field_set ( categ_fld, "" ) );
check_warning ( "set image field value",
empgui_c_image_set ( image_fld, & null_image ) );
}
The slide() routine is called when the Slide Mode toggle button is activated. First, it gets the state of the toggle, using empgui_c_toggleb_get(). If the toggle is on, then it calls next_loop() to fetch records continuously. If the toggle is off, then it stops the automatic fetching by removing the timer using empgui_c_timer_trash(), and makes sure that the scale window is hidden by setting the Show Scale toggle off using empgui_c_toggleb_set().
/*** Action for "slide_mode" togglebutton:
if togglebutton is on, start automatic
fetching of records
if togglebutton is off, stop automatic
fetching ***/
static void slide (obj, param)
object obj;
char*
param;
{
boolean slide_on;
void next_loop();
void pushb_sensitivity();
check_error (
"Get toggle value",
empgui_c_toggleb_get ( slide_tbn, & slide_on )
);
if (slide_on)
{
pushb_sensitivity (
false );
next_loop ();
}
else
{
check_error (
"Remove timer",
empgui_c_timer_trash (timerid)
);
check_warning (
"Set toggle off",
empgui_c_toggleb_set ( show_tbn, false )
);
pushb_sensitivity (
true );
}
}
The next_loop() routine fetches a record and displays it. Then it gets the value of the scale using empgui_c_scale_get(), and sets up a timer to call itself using empgui_c_timer_add().
/*** Fetch next record. If no next record, restart from first record.
Set timer to call same
function in 5 seconds ***/
static void next_loop ()
{
int state;
int secs;
state = mrtget(retrieval);
if (state==0)
{
mrgetend (retrieval);
if ((retrieval = mrtgtbegin
(ADDRNIL, record, ADDRNIL))
== ADDRNIL)
{
mr_error ("Select records");
}
state = mrtget(retrieval);
}
display (state, "Next");
check_error (
"Get scale value",
empgui_c_scale_get ( interval_scl, & secs )
);
/* set timer to fetch again in secs
seconds */
check_error (
"Set timer",
empgui_c_timer_add (secs * 1000, next_loop, (addr)0,
&timerid)
);
}
When going into slide mode, the Next and Previous buttons must be made insensitive. Also, the view cascade button must be made sensitive. The reverse is true when getting out of slide mode. The pushb_sensitivity() routine is called by the slide() routine, and it uses empgui_c_sens_set_all() to control the sensitivity of the the two pushbuttons, and empgui_c_sen_set() to control the sensitivity of the cascade button.
/*** Set the sensitivity of "Next" and "Prev" pushbuttons ***/
static void pushb_sensitivity
(on_or_off)
boolean
on_or_off;
{
object pbn_lst[3];
pbn_lst[0] = next_pbn;
pbn_lst[1] = prev_pbn;
pbn_lst[2] = (object)0;
check_warning (
"Set pushbutton sensitivities",
empgui_c_sens_set_all ( pbn_lst, on_or_off )
);
check_warning (
"Set View button sensitivity",
empgui_c_sen_set ( view_cbn, ! on_or_off )
);
}
The show_win() routine is called when activating the Show Scale toggle button in the view pulldown menu. It gets the state of the toggle using empgui_c_toggleb_get(). If the toggle is on, it displays the time_win window using empgui_c_window_show(). If the toggle is off, it hides the window using empgui_c_window_hide().
/*** Show or hide the scale window depending on the Show Scale toggle
***/
static void show_win (obj, param)
object obj;
char*
param;
{
boolean show_on;
check_error (
"Get toggle value",
empgui_c_toggleb_get ( show_tbn, & show_on )
);
if (show_on)
check_warning (
"Show scale window",
empgui_c_window_show ( time_win )
);
else
check_warning (
"Hide scale window",
empgui_c_window_hide ( time_win )
);
}
The exit_action() routine is only executed when exiting the module. In this case, it simply de-allocates the descriptors used, and closes the pictures table.
/*** Action executed when exiting this module: clean-up descriptors ***/
static void exit_action (obj, param)
object obj;
char*
param;
{
mrgetend (retrieval);
mrfrrec (record);
mrclose (table);
}
| File: | $EMPRESSPATH/rdbms/gui/examples/ex3_actn.c |
| Application Name: | ListApp |
| Module Names: | ListMain, ListSub |
| Description: | Displays records in a multilist. Double-clicking on the multilist causes a sub-module to appear, displaying the record clicked on. The sub-module allows deletion, update, insertion, as well as selection of records to be displayed in the multilist. |
| Database Interface: | Embedded SQL |
| Concepts Illustrated: | Use of multilist, Sensitivity of buttons, Using multiple modules. |
Since the application has two modules, the program will have two action tables. Both are defined at the beginning of the program.
/***********************************************************************
* (c) Copyright Empress Software Inc. 1983, 2006
***********************************************************************/
#include <guicc.h> /* for the GUI C API
routines */
#include <mscc.h> /* for embedded
SQL precompiler */
#define ACTION_TBL_NAME1
ListMain_action_table
#define ACTION_TBL_NAME2
ListSub_action_table
static void select_record ();
static void enter_main ();
static void exit_main ();
action_tab_entry ACTION_TBL_NAME1[] =
{
{ "enter_list", enter_main
}, /* Main module enter action */
{ "exit_list", exit_main
}, /* Main module exit action */
{ "select_record", select_record }, /*
'list' double click action */
{ CHARNIL }
};
static void enter_sub ();
static void exit_sub ();
static void clear ();
static void delete_rec ();
static void insert_rec ();
static void update_rec ();
static void select_list ();
action_tab_entry ACTION_TBL_NAME2[] =
{
{ "enter_record", enter_sub
}, /* Sub module enter action */
{ "exit_record", exit_sub
}, /* Sub module exit action */
{ "clear_fields", clear
}, /* 'Clear' pushbutton */
{ "delete",
delete_rec }, /* 'Delete' pushbutton */
{ "insert",
insert_rec }, /* 'Insert' pushbutton */
{ "update",
update_rec }, /* 'Update' pushbutton */
{ "select",
select_list }, /* 'Select' pushbutton */
{ CHARNIL }
};
Global variables are defined for objects that the program needs to access. Note that the Delete and Update pushbuttons are needed because the program needs to control their sensitivity.
static object list_obj; /* In main module: multilist */
static object name_fld; /* In sub module:
name field */
static object phone_fld; /* phone field */
static object position_fld; /* position field */
static object date_fld; /* date field */
static object delete_btn; /* delete pushbutton */
static object update_btn; /* update pushbutton */
The next section defines two constants for the select statements: the first query is for the main module, the second is for the sub module. The variables used in embedded SQL statements are in the SQL variable declaration section.
#define SELECT_1 "select name, position
from employee"
#define SELECT_2 "select name, phone, position,
date_hired
from employee where name = '%s'"
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
static char dbname[128];
/* Name of database */
static char query[1024];
/* Query to be prepared */
static char name_array[100][33];
/* Array of names and ... */
static char position_array[100][33]; /* ...positions
for multilist */
static char name_str[33];
/* Value of name attribute */
static char phone_str[15];
/* ...phone attribute */
static char position_str[128];
/* ...position attribute */
static char date_str[20];
/* ...date attribute */
static char* name_ptr;
/* Value of name field */
static char* phone_ptr;
/* ...phone field */
static char* position_ptr;
/* ...phone field */
static char* date_ptr;
/* ...phone field */
static short c1, c2, c3, c4;
/* Control variables */
EXEC SQL END DECLARE SECTION;
For dialog and error checking routines, we can use those defined in the SimpleApp module. So, these routines are declared as extern here. In addition, the check_sql_error() routine checks for error conditions after SQL operations.
/*** Dialog and Error checking routines ***/
extern void info_dialog
();
extern void error_dialog
();
extern boolean warning_dialog ();
extern void check_error
();
extern boolean check_warning ();
static void check_sql_error (str)
char*
str;
{
if (SQLCODE != 0)
error_dialog ( str,
SQLERRMC );
}
The enter_main() routine is executed when starting the main module. It performs SQL initialization, specifies the default database, prepares the query and declares a cursor for the query. Then it performs C API initialization and gets the main window using empgui_c_initialize() and the multilist object using empgui_c_obj_get_by_name().
/*** Routines for ListMain module ***/
/*** Enter action for ListMain module:
Initialize embedded SQL, prepare query
and declare cursor.
Initialize C API, get main window and
the list object. ***/
static void enter_main (obj, param)
object obj;
char*
param;
{
object mainwin;
void set_list ();
strcpy (dbname, empgui_c_dbname);
EXEC SQL INIT;
check_sql_error ("SQL initialize");
EXEC SQL DATABASE IS :dbname;
check_sql_error ("Database initialize");
strcpy (query, SELECT_1);
EXEC SQL PREPARE s1 FROM :query;
check_sql_error ("Prepare query");
EXEC SQL DECLARE c1 CURSOR FOR s1;
check_sql_error ("Declare cursor");
check_error (
"Initialize",
empgui_c_initialize ( & mainwin, (object*) 0, CHARNIL)
);
check_error (
"Get list object",
empgui_c_obj_get_by_name ( mainwin, "list", & list_obj )
);
set_list ();
}
The set_list() routine selects and fetches all the records specified in the first cursor. The values of name and position attributes are stored in arrays of character arrays. However, empgui_c_list_set_items(), the function to assign items to a list or multilist, takes arrays of pointers to strings. So, two arrays of character pointers are set up to point to each element of the data arrays, and these arrays of pointers are passed to empgui_c_list_set_items().
/*** Open the cursor, fetch all the records, put the values of name
and position attributes in the multilist,
and close the cursor ***/
static void set_list ()
{
int i;
int num_recs;
char* list1[100];
char* list2[100];
EXEC SQL OPEN c1;
check_sql_error ("Open cursor");
for (i=0; (i<100) && (SQLCODE==0);
i++)
{
EXEC SQL FETCH c1 INTO
:name_array[i] :c1,
:position_array[i] :c3;
if (SQLCODE != 100)
check_sql_error ("Fetch");
if (c1 < 0)
name_array[i][0] = (char)0;
if (c3 < 0)
position_array[i][0] = (char)0;
list1[i] = name_array[i];
list2[i] = position_array[i];
}
if (i == 100)
warning_dialog ("Employee
List", "Too many records");
num_recs = i 1; /* as
there was no record when i was last
incremented */
check_warning (
"Set list contents",
empgui_c_list_set_items ( list_obj, num_recs,
list1, list2, (char**)0 )
);
EXEC SQL CLOSE c1;
check_sql_error ("Open cursor");
}
When the user double-clicks on a row of the multilist, that row is selected, and the sub module is called to display the corresponding record. The select_record() routine gets the selected row number of the multilist using empgui_c_prop_get(), gets the corresponding name from the array of names, and calls the sub module using empgui_c_module_run(), passing it the name as argument.
/*** Get the name in the selected row of the multilist,
and call the ListSub
module ***/
static void select_record (obj, param)
object obj;
char*
param;
{
int row;
char* argv[1];
check_error (
"Get selected row in
list",
empgui_c_prop_get (
obj, PROP_VALUE, & row )
);
if (row < 1)
{
warning_dialog ("List",
"Invalid row selected");
return;
}
argv[0] = name_array[row-1];
check_error (
"call module",
empgui_c_module_run
( "ListSub", 1, argv)
);
}
The exit action of the main module has to clean up data structures used by embedded SQL. However, if EXEC SQL EXIT is performed, then all Empress data structures will be cleaned up, including those used by Empress GUI Builder. Therefore, the special statement EXEC SQL EXIT_SQL is used here - it only cleans up embedded SQL data structures.
/*** Cleanup embedded SQL data structures before exiting the main
module
***/
static void exit_main (obj, param)
object obj;
char*
param;
{
EXEC SQL EXIT_SQL;
}
The enter action for the sub module has four arguments, instead of the usual two, because the name from the selected row is passed in argv[0]. It uses the name to build a query to fetch that record only. Note that the sub module's enter action also needs to call empgui_c_initialize() to get its main window. Then it gets the fields and pushbuttons needed using empgui_c_objs_get_by_name_pair(). The values of the attributes of the fetched record are displayed in the corresponding fields using empgui_c_field_set().
/*** Routines for ListSub module ***/
/*** Enter action for ListSub module.
Initialize C API, get main window, fields
and pushbuttons.
Prepare query and fetch record selected
from multilist. ***/
static void enter_sub (obj, param,
argc, argv)
object obj;
char*
param;
int
argc;
char*
argv[];
{
static object mainwin2;
sprintf (query, SELECT_2, argv[0]);
EXEC SQL PREPARE s2 FROM :query;
check_sql_error ("Prepare query");
/* module may be called many times, but only
need initialization once */
if (mainwin2 == (object)0)
{
check_error (
"Initialize submodule",
empgui_c_initialize ( & mainwin2, (object*) 0,
CHARNIL )
);
check_error (
"Get fields objects",
empgui_c_objs_get_by_name_pair ( mainwin2,
"name", & name_fld,
"phone", & phone_fld,
"position", & position_fld,
"date_hired", & date_fld,
"deleteb", & delete_btn,
"updateb", & update_btn,
CHARNIL )
);
EXEC SQL DECLARE c2
CURSOR FOR s2 FOR UPDATE;
check_sql_error ("Declare
cursor");
}
EXEC SQL OPEN c2;
check_sql_error ("Open cursor");
EXEC SQL FETCH c2 INTO :name_str :c1, :phone_str
:c2,
:position_str :c3, :date_str :c4;
clear (obj, param);
if (SQLCODE != 100)
check_sql_error ("Fetch");
if (c1 < 0)
check_warning ( "Set
field value",
empgui_c_field_set ( name_fld, name_str ) );
if (c2 < 0)
check_warning ( "Set
field value",
empgui_c_field_set ( phone_fld, phone_str ) );
if (c3 < 0)
check_warning ( "Set
field value",
empgui_c_field_set ( position_fld, position_str ) );
if (c4 < 0)
check_warning ( "Set
field value",
empgui_c_field_set ( date_fld, date_str ) );
}
To get the the contents of each field, the function empgui_c_field_get() is used.
/*** Get the contents of the fields ***/
static void read_fields ()
{
check_error ( "Get field value",
empgui_c_field_get (
name_fld, & name_ptr ) );
check_error ( "Get field value",
empgui_c_field_get (
phone_fld, & phone_ptr ) );
check_error ( "Get field value",
empgui_c_field_get (
position_fld, & position_ptr ) );
check_error ( "Get field value",
empgui_c_field_get (
date_fld, & date_ptr ) );
}
The select_list() routine is called when the Select button is clicked. It builds conditions for the WHERE clause of the first query (for the multilist), matching attributes to fields. Note that the new query is prepared again, so that it is associated with prepared statement s1, but the cursor c1 does not have to be declared again - it is already associated with statement s1. Finally, the sub module is exited using empgui_c_module_exit().
/*** Modify the query for the main module by building a WHERE clause
matching the contents of fields to attributes,
and exit the sub
module ***/
static void select_list (obj, param)
object obj;
char*
param;
{
int num_conds = 0;
read_fields ();
strcpy (query, SELECT_1);
if (strlen (name_ptr) != 0)
{
num_conds ++ ;
strcat (query, " where
name match '");
strcat (query, name_ptr);
strcat (query, "'");
}
if (strlen (phone_ptr) != 0)
{
num_conds ++ ;
if (num_conds==1)
strcat (query, " where");
else
strcat (query, " and");
strcat (query, " phone
match '");
strcat (query, phone_ptr);
strcat (query, "'");
}
if (strlen (position_ptr) != 0)
{
num_conds ++ ;
if (num_conds==1)
strcat (query, " where");
else
strcat (query, " and");
strcat (query, " position
match '");
strcat (query, position_ptr);
strcat (query, "'");
}
if (strlen (date_ptr) != 0)
{
num_conds ++ ;
if (num_conds==1)
strcat (query, " where");
else
strcat (query, " and");
strcat (query, " date_hired
= '");
strcat (query, date_ptr);
strcat (query, "'");
}
EXEC SQL PREPARE s1 FROM :query;
check_sql_error ("Prepare query");
empgui_c_module_exit ();
}
The clear() routine is called when the Clear button is clicked. It empties the fields by assigning empty strings using empgui_c_field_set().
/*** Clear the contents of the fields ***/
static void clear (obj, param)
object obj;
char*
param;
{
check_warning ( "Set field value",
empgui_c_field_set (
name_fld, "" ) );
check_warning ( "Set field value",
empgui_c_field_set (
phone_fld, "" ) );
check_warning ( "Set field value",
empgui_c_field_set (
position_fld, "" ) );
check_warning ( "Set field value",
empgui_c_field_set (
date_fld, "" ) );
}
After deleting a record, there is no longer a current record. And after inserting a new record, the new record is not the current record. Hence, the delete and update operations should be disabled by making their pushbuttons insensitive, using empgui_c_sen_set().
/*** Make the 'Delete' and 'Update' buttons insensitive ***/
static void make_insensitive ()
{
check_warning (
"Set delete button insensitive",
empgui_c_sen_set ( delete_btn,
false )
);
check_warning (
"Set update button insensitive",
empgui_c_sen_set ( update_btn,
false )
);
}
/*** Delete the current record ***/
static void delete_rec (obj, param)
object obj;
char*
param;
{
EXEC SQL DELETE FROM employee WHERE CURRENT
OF c2;
check_sql_error ("Delete");
clear (obj, param);
make_insensitive ();
}
/*** Insert a new record ***/
static void insert_rec (obj, param)
object obj;
char*
param;
{
read_fields ();
EXEC SQL INSERT INTO employee (name, phone,
position, date_hired)
VALUES (:name_ptr, :phone_ptr,
:position_ptr, :date_ptr);
check_sql_error ("Insert");
make_insensitive ();
}
/*** Update the current record ***/
static void update_rec (obj, param)
object obj;
char*
param;
{
read_fields ();
EXEC SQL UPDATE employee SET name = :name_ptr,
phone = phone_ptr,
position = :position_ptr,
date_hired = :date_ptr
WHERE CURRENT OF c2;
check_sql_error ("Update");
}
The exit action of the sub module closes the cursor used to fetch the record displayed and makes the Update and Delete buttons sensitive again (in case they were de-sensitized after an insert or delete operation). Then it calls set_list() to update the multilist. This will cause any delete, update or insert to be reflected in the multilist, and also if the Select button was used to select only records matching the fields.
/*** Before quitting the sub module, close the cursor that it used,
re-select the cursor for the main
module, and re-fill the list ***/
static void exit_sub (obj, param)
object obj;
char*
param;
{
EXEC SQL CLOSE c2;
check_sql_error ("Close cursor");
check_warning (
"Set delete button sensitive",
empgui_c_sen_set ( delete_btn, true )
);
check_warning (
"Set update button sensitive",
empgui_c_sen_set ( update_btn, true )
);
set_list ();
}
| File: | $EMPRESSPATH/rdbms/gui/examples/ex4_actn.c |
| Application Name: | ExamResults |
| Module Name: | ExamResults |
| Description: | Displays a bar chart showing the distribution of grades obtained by students in a particular subject. A pushbutton allows the results of the next subject to be displayed. |
| Database Interface: | Embedded SQL |
| Concepts Illustrated: | Using the user object as a drawing area. |
The program needs to include several X11 and Xm header files because of the X and Motif functions used.
/***********************************************************************
* (c) Copyright Empress Software Inc. 1983, 2006
***********************************************************************/
#include <guicc.h> /* for the GUI C API
routines */
#include <mscc.h> /* for the embedded
SQL precompiler */
#include <X11/Xlib.h> /* for the X and Motif calls */
#include <X11/Intrinsic.h>
#include <Xm/Xm.h>
#define ACTION_TBL_NAME EResult_action_table
static void next ();
static void enter_action ();
static void exit_action ();
static void user_init ();
action_tab_entry ACTION_TBL_NAME[] =
{
{ "next_subject", next
}, /* 'Next Subject' pushbutton */
{ "enter",
enter_action }, /* Module enter action */
{ "exit",
exit_action }, /* Module exit action */
{ "user_obj_init", user_init
}, /* User object init action */
{ CHARNIL }
};
Variables used in embedded SQL statements are in the SQL variable declaration section. Global variables are defined for GUI and X objects, and also for data that needs to be accessed by the routines which fetch data and display the data.
/*** Embedded SQL declarations and other global variables ***/
#define Select_Results "select grade, count
from exam_results
where subject = '%s' group by grade"
EXEC SQL INCLUDE SQLCA;
EXEC SQL BEGIN DECLARE SECTION;
static char dbname[128];
/* Name of current database */
static char subjects[20][16]; /* Names of subjects
studied */
static char grades[10][8]; /* All
the possible grades */
static char query[128];
/* SQL select statement */
static char grade[8];
/* Value of grade attribute */
static int num;
/* No of records with the same
grade */
EXEC SQL END DECLARE SECTION;
static Display *this_display;
static Widget DrawingArea_widget;
static GC
DrawingArea_GC;
static object subject_fld;
static int num_subjects;
/* Number of subjects available */
static int num_grades;
/* Number of possible grades */
static int current_subject;
static int results[10];
/* No of students for each grade */
static char results_str[10][8]; /* ... in string
format */
The error checking routines defined in modules SimpleApp and ListApp will be used.
/*** Error Checking Routines ***/
extern void error_dialog ();
extern void check_error ();
extern void check_sql_error ();
The enter_main() routine is the module enter action. It performs all the necessary database initialization.
/*** Initialization: Find what subjects and what grades are available
***/
static void enter_action (obj, param)
object obj;
char*
param;
{
object mainwin;
int i;
void get_results();
EXEC SQL INIT;
check_sql_error ("SQL initialize");
strcpy (dbname, empgui_c_dbname);
EXEC SQL DATABASE IS :dbname;
check_sql_error ("Database initialize");
A query is formulated to find all the subjects available. The names of the subjects are stored in global array subjects[].
EXEC SQL DECLARE c_subjects CURSOR FOR
"select distinct subject
from exam_results sort by
subject";
check_sql_error ("Declare cursor");
EXEC SQL OPEN c_subjects;
check_sql_error ("Open cursor");
for (i=0; (i<20) && (SQLCODE==0);
i++)
{
EXEC SQL FETCH c_subjects
INTO :subjects[i];
if (SQLCODE != 100)
check_sql_error ("Fetch");
}
num_subjects = i 1;
EXEC SQL CLOSE c_subjects;
Another query is formulated to find all the possible grades. These are stored in global array grades[].
EXEC SQL DECLARE c_grades CURSOR FOR
"select distinct grade
from exam_results sort by grade";
check_sql_error ("Declare
cursor");
EXEC SQL OPEN c_grades;
check_sql_error ("Open cursor");
for (i=0; (i<20) && (SQLCODE==0);
i++)
{
EXEC SQL FETCH c_grades
INTO :grades[i];
if (SQLCODE != 100)
check_sql_error ("Fetch");
}
num_grades = i-1;
EXEC SQL CLOSE c_grades;
After initializing the C API and getting the subject field, a query is prepared to get the count of records with the same grades for the first subject.
/* Perform C API initialization */
check_error (
"Initialize",
empgui_c_initialize
( & mainwin, (object*) 0, CHARNIL)
);
check_error (
"Get field objects",
empgui_c_obj_get_by_name
(mainwin, "subject",
&subject_fld)
);
/* Formulate the query to get the results for first subject */
current_subject = 0;
sprintf (query, Select_Results, subjects[current_subject]);
EXEC SQL PREPARE s_results FROM :query;
EXEC SQL DECLARE c_results CURSOR FOR s_results;
get_results();
}
The get_results() routine gets the grades (and the their counts) for the current subject, and stores their counts into integer global array results[], and string global array results_str[]. The routine has to handle cases where there is no record for a particular grade for the current subject, in which case the count should be set to zero. The current subject name is displayed in the corresponding field.
/*** Get distribution of results for the current subject ***/
static void get_results()
{
int i, j;
EXEC SQL OPEN c_results;
check_sql_error ("Open
cursor");
for (i=0, j=0; (j<num_grades)
&& (SQLCODE==0); i++, j++)
{
EXEC SQL FETCH c_results INTO :grade, :num;
if (SQLCODE != 100)
{
check_sql_error ("Fetch");
while (strcmp (grade, grades[j]) != 0)
{
strcpy (results_str[j], "0");
results[j++] = 0;
if (j == num_grades)
{
fprintf (stderr,"Grade %s unknown!\n",
grade);
msexit ();
}
}
sprintf (results_str[j], "%d", num);
results[j] = num;
}
}
if (SQLCODE == 100)
for (i=j 1; i<num_grades; i++)
{
results[i] = 0;
strcpy (results_str[i], "0");
}
EXEC SQL CLOSE c_results;
check_error (
"Set field value",
empgui_c_field_set ( subject_fld,
subjects[current_subject] )
);
}
The user_init() routine gets the widget for the user object. Then it uses several Xlib and Xt routines to get the display, to set the callbacks for the resize and expose events, and to create a graphic context.
/*** Perform C API Ininitialization, get field
and user object
and get the display,
drawing area widget, window, and gc ***/
static void user_init
(obj, param)
object obj;
char* param;
{
XGCValues
GC_vals;
void
draw_chart();
check_error (
"Get user widget",
empgui_c_user_get_widget (obj, &DrawingArea_widget)
);
this_display = XtDisplay
(DrawingArea_widget);
XtAddCallback (DrawingArea_widget,
XmNresizeCallback,
draw_chart, 0);
XtAddCallback (DrawingArea_widget,
XmNexposeCallback,
draw_chart, 0);
XtVaGetValues (DrawingArea_widget,
XtNforeground,
&(GC_vals.foreground), XtNbackground,
&(GC_vals.background), NULL);
GC_vals.line_width =
4;
GC_vals.font = XLoadFont
(this_display,
" * courier bold * * 14 100 100 100 * * * *");
DrawingArea_GC = XtGetGC
(DrawingArea_widget,
GCForeground | GCBackground | GCLineWidth | GCFont,
&GC_vals);
}
The draw_object() routine gets the height and width of the user widget. Then it finds the highest count for the grade results. Using the user widget as a drawing area, it writes the grade, draws a rectangle of a width proportional to the corresponding count, and writes the count for each grade. Note that the height of the rectangles and the vertical distance between them depend on the number of possible grades and the height of the user widget.
/*** Draw the chart to illustrate the distribution
of results,
use the user object
as a canvas for drawing text and rectangles ***/
static void draw_chart (w, client_data, call_data)
{
int
i;
int
highest;
int
ypos;
int
height_rect;
Dimension DrawingArea_width;
Dimension DrawingArea_height;
Window
DrawingArea_window;
DrawingArea_window =
XtWindow (DrawingArea_widget);
XClearWindow (this_display,
DrawingArea_window);
XtVaGetValues (DrawingArea_widget,
XmNwidth,
&DrawingArea_width, XmNheight,
&DrawingArea_height, NULL);
highest = results[0];
for (i=0; i < num_grades;
i++) /* Find the highest number */
{
if (results[i] > highest)
highest = results[i];
}
if (highest == 0) /*
To avoid division by zero */
highest = 1;
if ( DrawingArea_height
< 100 ) /* Set min size for area */
DrawingArea_height = 100;
if ( DrawingArea_width
< 100 )
DrawingArea_width = 100;
height_rect = DrawingArea_height/(num_grades*2+1);
for (i=0; i < num_grades;
i++) /* For each possible grade */
{
ypos = (2*i+1)*DrawingArea_height/(2*num_grades+1);
/* Write the grade on the left side of the drawing area */
XDrawString (this_display, DrawingArea_window,
DrawingArea_GC,
20, ypos + height_rect, grades[i],
strlen(grades[i]));
/* Draw horizontal bar to represent students with that
grade */
XFillRectangle (this_display, DrawingArea_window,
DrawingArea_GC, 50, ypos, (DrawingArea_width -
90)* results[i]/highest, height_rect);
/* Write no of students on the right side of the drawing
area */
XDrawString (this_display, DrawingArea_window,
DrawingArea_GC,
DrawingArea_width - 30, ypos + height_rect,
results_str[i], strlen(results_str[i]));
}
XFlush (this_display);
}
The next() routine is executed when the Next Subject pushbutton is activated. It changes the query to get the results for the next subject, calls get_results() to retrieve the results, and calls draw_chart() to display the results.
/*** Display results for the next subject ***/
static void next
(obj, param)
object obj;
char* param;
{
/* Formulate the query
to get the results for next subject */
if (++current_subject
== num_subjects)
current_subject = 0;
sprintf (query, Select_Results,
subjects[current_subject]);
EXEC SQL PREPARE s_results
FROM :query;
get_results();
draw_chart();
}
Finally, the exit_action() routine cleans up the embedded SQL data structures before the module is terminated.
/*** Cleanup SQL data structures before exiting ***/
static void exit_action
(obj, param)
object obj;
char* param;
{
EXEC SQL EXIT_SQL;
}
| File: | $EMPRESSPATH/rdbms/gui/examples/ex5_actn.c |
| Application Name: | FlatFileApp |
| Module Name: | FlatFileApp |
| Description: | Reads a flat file and displays the result in a multilist. |
| Concepts Illustrated: | Reading flat files, Getting objects, Use of mulitlist |
/***********************************************************************
* (c) Copyright Empress Software Inc. 1983, 2006
***********************************************************************/
#include <guicc.h>
#include <mscc.h>
#define CURRENT_MODULE "FlatFileApp"
#define ACTION_TBL_NAME FlatFile_action_table
#define SEPCHARS "\t\n"
/* list of separation characters */
/* in the flat file */
static void mlist_setup ();
action_tab_entry ACTION_TBL_NAME[] =
{
{ "enter_mlist", mlist_setup },
{ CHARNIL }
};
This function reads in a flat file, one record at a time. It then splits the record to correspond to the number of columns of the multilist and store it in appropriate arrays. Finally, it calls the C API functions to display the data in the multilist.
static void mlist_setup (obj, param)
object obj;
char*
param;
{
FILE *flatfile;
/* file pointer to the flat file */
char *filename="flatfile"; /* name of
the flat file */
char *records[256];
/* array of records. In this case */
/* 256 is the maximum number of
records */
/* that can be read */
char *listval0[256];
/* four arrays corresponding to the
four */
char *listval1[256];
/* columns of the multi list */
char *listval2[256];
char *listval3[256];
char *s;
/* pointer to indicate when fgets
finishes */
int num_rec;
/* actual number of records read */
int i=0, j=0;
/* loop counters */
object mainwin;
object mlistobject;
gui_status status;
Open the flatfile with appropriate error checking.
flatfile = fopen(filename,"r");
if (flatfile == NULL)
{
fprintf(stderr,"Cannot
open the %s\n", filename);
exit (0);
}
/* allocate memory for each of the records */
for ( i = 0; i < 256; i++ )
records[i] = (char *)
malloc ( 80 * sizeof(char) );
Read one line ( record in this case ) at a time till the end of the file is reached. Upper limit of number of records that can be read in this case is set to be 256. Read each record and split it into four elements to correspond to the four columns of multilist and store it into appropriate arrays.
j = 0;
while ( s != NULL )
{
s = fgets ( records[j],
80, flatfile );
listval0[j] = (addr
) strtok (records[j], SEPCHARS);
listval1[j] = (addr
) strtok (NULL, SEPCHARS);
listval2[j] = (addr
) strtok (NULL, SEPCHARS);
listval3[j] = (addr
) strtok (NULL, SEPCHARS);
j++;
}
/* set the number of records read from the flat
file */
num_rec = j 1;
After getting the data from the flat file in appropriate errors, the main window and the multilist is obtained.
status = empgui_c_bin_get_by_name("MAINWIN",&mainwin);
if (status != E_C_SUCCESS) {
fprintf (stderr, "enter
error 10: %d\n", status);
}
status = empgui_c_obj_get_by_name (mainwin, "multilist",
&mlistobject);
if (status != E_C_SUCCESS) {
fprintf (stderr, "enter
error 20: %d\n", status);
}
Finally the arrays filled with data from the flat file is displayed in the multilist.
status = empgui_c_list_set_items (mlistobject,
num_rec, listval0,
listval1, listval2, listval3, 0);
if (status != E_C_SUCCESS) {
fprintf (stderr, "enter
error 20: %d\n", status);
}
}