# CHAPTER 7: Examples

## 7.1 Overview

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.

## 7.2 Creating the Example Database

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.

## 7.3 Compiling and Running the Examples

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, 2003'`

`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/mkexgui

Run 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

## 7.4 The ex_actab.c File

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;`

## 7.5 Example 1: SimpleApp

`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.

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, 2003`
`**********************************************************************/`

`#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 ) );`
`}`

## 7.6 Example 2: Images_c_api

`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, 2003`
`***********************************************************************/`

`#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);`
`}`

## 7.7 Example 3: ListApp

`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, 2003`
`***********************************************************************/`

`#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 ();`
`}`

## 7.8 Example 4: ExamResults

`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, 2003`
`***********************************************************************/`

`#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;`
`}`

## 7.9 Example 5: FlatFileApp

`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

Since the application only has one action, everything is done in this action. The flat file is read and the multilist arrays are correctly filled. Finally the data is displayed in the multilist.

`/***********************************************************************`
`* (c) Copyright Empress Software Inc. 1983, 2003`
`***********************************************************************/`

`#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);`
`}`
`}`