************************************* * * * DB/C Newsletter * * April 1996 * * * ************************************* Editor's Notes This month's article is about writing GUI programs with DB/C 9.0. One of the new features of DB/C 9.0 is the Fast Development Environment (FDE) which contains a GUI dialog painter used to design GUI windows. The output of this design process is DB/C code fragments, but not complete programs. This article describes several programs that use these program fragments to create complete, functioning GUI programs. In embarking on writing this article and its supporting code, I decided to use an object-oriented approach instead of the traditional loadmod-based approach. I was surprised at how well the OO approach worked - but only after I added two features to DB/C 9.0. These features are: the getobject statement and the test statement with an object variable. Unfortunately, these features did not get into the initial distributions of DB/C 9.0. If you want to use the code described in this month's article, you need to get the latest version of DB/C 9.0. The use of these two features is described in the Read Me First document dated April 15, 1996 or later. don.wills@swc.com Object-Oriented FDE Support Code Among its several features, the Fast Development Environment (FDE) tool that is part of DB/C 9.0 can be used to design graphical user interface (GUI) resources. These resources are dialogs, panels, icons and menus. FDE stores these resources into text files in the form of DB/C INIT statements. These text files can be used either as DB/C source programs or include files. The INIT statements may be used by custom code that creates (PREPs) the resource using the INIT statements. Instead of using custom code, in this article we describe several object-oriented user verbs that use the INIT statements created by FDE. You don't need the DB/C source code to understand the discussion in this article, but it might be helpful. The DB/C source code may be found at the Subject, Wills & Company FTP server (ftp.swc.com) in /pub/misc/fdeoop. The FILEIO class (the source file is fileio.cls) contains several user defined verbs that simplify file access. FILEIO isn't directly related to FDE-produced code, but is used in several of the FDE support classes. The FILEIO class verbs work with a specially formatted data structure and an object variable. Here is an example of this data structure: ********************************************************** . customer master file definition cst object cst.data list cst.record list cst.cust char 6 1- 6 Customer Number cst.name char 40 7- 46 Customer Name* cst.addr char 30[2] 47- 106 Address cst.city char 15 107- 121 City* cst.stcd char 2 122- 123 State* cst.zpcd char 10 124- 133 Zip Code* cst.coun char 15 134- 148 Country* cst.cont char 30 149- 178 Contact Name* cst.phon char 13 179- 191 Telephone cst.faxn char 13 192- 204 Fax Number cst.comment char 200 205- 404 Comments listend cst1 list ifile keylen=6, fix=404 int 1 varlist "cstfile" varlist cst.cust listend cstaim list afile fix=404 int 1 varlist "cstfile" listend listend ******************************************************* This example provides support for a customer file that has a primary index on the six character customer number and has an AIM index on several fields (the fields marked with an asterisk). The cst.data list/listend data group defines the FILEIO class data structure. Within it are multiple list/listend groups. The first group contains the record variables. The remaining list/listend groups contains file, ifile and afile variable definitions, file names and key fields. The int 1 variable immediately before the file name is an indicator of whether the file is in the open or closed state. It is not typically used by the calling program. The FILEIO class contains methods that implement user defined verbs with names like fopen, fclose, fread, freadnext, fwrite, fupdate, fdelete, etc. Here is some sample DB/C code that uses these verbs: ******************************************************* fopen cst, cst.data move "123456" to cst.cust fread cst move "(630) 572-0240" to cst.phon fupdate cst aimkeys char 20[6] clear aimkeys move "01FSUB" to aimkeys[1] move "02XIL" to aimkeys[2] freadaim cst, aimkeys loop break if over call displayrec freadaimnext cst repeat fclose cst *********************************************** The fopen method is the only !make method for the FILEIO class. !make methods instantiate the object variable, in this case the variable named cst. The fclose method is a !destroy method which uninstantiates the object variable. All other methods assume that the object variable is already instantiated, so you always need to call the fopen method before any other methods. The source file, fileio.cls, is compiled into a .dbc file that contains the fileio class. The other important file is fileio.txt. This file contains the class reference and all of the user defined verb prototype definitions. This file should be included in any source program that uses the FILEIO class. The next class we will discuss is the DIALOG class. The DIALOG class contains two methods - dialog and msgdialog. These are !transient methods which means that the calling program does not need to allocate an object variable for them because the instance of the class will be transient. A transient instance disappears when the method returns. Thus there is no long term state or information storage. The msgdialog method is used to communicate simple messages to the user. For example, the FILEIO class code uses the msgdialog method to communicate unrecoverable error conditions. Here are two examples: *********************************************** msgdialog "RECORD MUST BE A LIST OR CHAR VARIABLE" msgdialog ("UNABLE TO OPEN " + filename) *********************************************** The msgdialog method displays the text in a dialog box. The dialog box also contains an OK button used to dismiss the dialog and continue processing. The dialog method allows for text fields, radio buttons, check boxes and more controls in the transient dialog. Here is an example: ************************************************ include dialog . Customer SearchDialog CUSTSRCH init "DIALOG=CUSTSRCH,TITLE=Customer Search,SIZE=320:24": "0,H=50,V=10,TEXT=Name\:,H=50,V=40,TEXT=City\:,H=50": ",V=70,TEXT=State\:,H=50,V=100,TEXT=Zip\:,H=50,V=13": "0,TEXT=Country\:,H=50,V=160,TEXT=Contact\:,H=110,V": "=10,EDIT=101::140,H=110,V=40,EDIT=102::140,H=110,V": "=70,EDIT=103::40,H=110,V=100,EDIT=104::80,H=110,V=": "130,EDIT=105::140,H=110,V=160,EDIT=106::140,H=70,V": "=200,PUSHBUTTON=1:Search:70:30,H=180,V=200,PUSHBUT": "TON=2:Cancel:70:30" button int 3 dialog CUSTSRCH, f=101:cst.cust, f=102:cst.city, f=103:cst.stcd: f=104:cst.zpcd, f=105:cst.coun, f=106:cst.cont, pbid=button call search if (button = 1) ******************************************************* In this example, the CUSTSRCH variable was created by the FDE dialog painter. The other lines of code were hand coded. The example dialog allows the operator to provide search keys for each of the fields that are aim keys in the customer file. Each of the fields is an edit control with id from 101 through 106. The f= operands in the dialog verb link the edit controls with fields in the customer record. The dialog also contains two buttons - Search and Cancel. The Search button control id is one and the Cancel button control id is two. The variable specified by the pbid operand of the dialog verb will contain the push button id of the push button that closed the dialog. This example shows only the use of edit fields, but the other types of controls supported by DB/C can be used also. The DIALOG class methods - dialog and msgdialog - are modal. This means that input is only allowed to the dialog window and attempts to activate other windows via keystrokes or mouse clicks are ignored. When multiple windows are active, non-modal behavior is preferred. The DB/C programming model for the GUI is based on messages passed to the program through a queue variable. For the program to operate non-modally, messages must be sent to a single queue variable, and then some sort of distribution method must be used to send messages to the appropriate code. The source file named main.mod contains code that does just that. main.mod compiles into a loadmod that should either be preloaded or loaded before any of the classes that use it to distribute messages are instantiated. The two other general purpose classes we will discuss in this article are MENU and MAINT. The MENU class supports a floating menu box that works non-modally with the main loadmod message distribution functions. The MAINT class supports a file maintenance type window that also works non-modally in conjunction with main. The MENU class has one method called addmenuitem. The make statement is used to instantiate the MENU class and the addmenuitem method is used to add menu items to the dialog box. The MAINT class has three methods - maint, processmsg and frontwindow. The maint method is the initialization method. Note, that maint is not a make method, but is meant to be called by derived (child) classes that are specific for a given file. The processmsg method processes all messages which in this case are received by the main loadmod and sent to the appropriate object via its processmsg method. The frontwindow method makes this window be the frontmost window. Here is the a complete program that gives an example of how the MENU and MAINT classes are used. This example program is start.prg: ************************************************************ inc main inc menu inc dialog inc cstmaint inc trmmaint startmenu object cmobj1 object cmobj2 object cmobj3 object cmobj4 object tmobj object obj object @ i1 int 3 . start of main program loadmod "main" make startmenu from menu addmenuitem startmenu, "Customer File Maintenance", cstmaint addmenuitem startmenu, "Terms Code File Maintenance", trmmaint addmenuitem startmenu, "Enter Invoices", invtrans main_dispatch shutdown . start a customer maintenance window cstmaint lroutine for i1 from 1 to 4 loadadr obj from i1 of cmobj1, cmobj2, cmobj3, cmobj4 test obj if over make obj from cstmaint return endif repeat frontwindow cmobj1 return endroutine . start a terms code file maintenance window trmmaint lroutine test tmobj if over make tmobj from trmmaint else frontwindow tmobj endif return endroutine . start an invoice entry window invtrans lroutine msgdialog "NOT SUPPORTED" return endroutine ******************************************************************************* The make startmenu and the addmenuitem statements cause a floating menu to be displayed. The last parameter of each of the addmenuitem statements is the routine label that is called when that particular menu item is clicked. The main_dispatch verb processes all messages. It should never return. The cstmaint routine instantiates up to four object variables using the CSTMAINT class. This is a special class that contains the window layout and other information associated with the customer file maintenance function. CSTMAINT is a child of the MAINT class. You might notice the use of the test statement to ascertain whether an object variable is instantiated or not. This behavior is not documented in the DB/C 9.0 Programmer's Reference. See the current version of the Read Me First document for details. Here is the CSTMAINT class code (the file is trmmaint.cls): ******************************************************************************* inc maint inc fileio . customer maintenance program class cstmaint class definition, make=make, parent=maint inc cstdef this object @ . Customer Maintenance Panel cstpanel init "PANEL=cstpanel,H=10,V=10,TEXT=Customer Number\:,H=": "10,V=40,TEXT=Customer Name\:,H=10,V=70,TEXT=Addres": "s line 1\:,H=10,V=100,TEXT=Address line 2\:,H=10,V": "=130,TEXT=City\, State\, Zip\:,H=10,V=160,TEXT=Cou": "ntry\:,H=10,V=190,TEXT=Contact Name\:,H=10,V=220,T": "EXT=Telephone\:,H=10,V=250,TEXT=Fax\:,H=260,V=160,": "TEXT=Comments\:,H=130,V=10,EDIT=1::60,H=130,V=40,E": "DIT=2::180,H=130,V=70,EDIT=3::180,H=130,V=100,EDIT": "=4::180,H=130,V=130,EDIT=5::120,H=260,V=130,EDIT=6": "::40,H=310,V=130,EDIT=7::80,H=130,V=160,EDIT=8::12": "0,H=130,V=190,EDIT=9::120,H=130,V=220,EDIT=10::100": ",H=130,V=250,EDIT=11::100,H=260,V=180,MEDIT=12::16": "0:90" make lroutine getobject this fopen cst, cst.data goto terminate if over maint this, cst, "Customer File Maintenance", cstpanel: f=1:cst.cust, f=2:cst.name, f=3:cst.addr[1], f=4:cst.addr[2]: f=5:cst.city, f=6:cst.stcd, f=7:cst.zpcd, f=8:cst.coun: f=9:cst.cont, f=10:cst.phon, f=11:cst.faxn, f=12:cst.comment: keyfld="Customer Number":cst.cust return terminate destroy this return endroutine endclass ******************************************************************************* There two important things to notice about this class. The first is the INIT statement that defines the layout of the controls in the window. This INIT statement was created by the FDE panel painter. It can also be modified in place by the FDE panel painter. The second important thing to notice is the maint method call into the parent class which is also named maint. Notice how the maint method links the window controls with the record variables. As each control in the window is changed, the associated variable is also changed. The maint method call depends on the getobject statement which is not documented in the DB/C 9.0 Programmer's Reference. The getobject statement moves the address of the object variable that is currently active into the destination variable, which must be an object pointer variable. See the current version of the Read Me First document for a complete description. One of the primary functions of FDE is to simplify writing GUI programs. But to use FDE effectively, additional support code is required. The classes described in this article provide the support code needed to simplify the DB/C 9.0 GUI program development process. DB/C Class Schedule Class Date Location DB/C Fundamentals April 10 through April 12 Oak Brook, IL DB/C Advanced Features April 17 through April 19 Oak Brook, IL For information, contact Judi Tamkevic at: voice 708.572.0240 fax 708.572.0390 email dbc@swc.com