************************************* * * * DB/C Newsletter * * March 2000 * * * ************************************* News and Comments We've had several requests for a working example of a GUI program that does file maintenance (that is, add/change/delete of records in a file). This month's newsletter describes such a program. I tried to make the sample GUI program as simple and straightforward as possible so that you could easily understand how things work. Thus I didn't use any include files or prebuilt user verbs. Whew! It made the effort much harder. The result is a program of some 800 lines that isn't complete (note the use of a routine named "notsupported") and also has several bugs. To me, this endeavor reinforced the importance of reusing code. I feel sorry for anyone still writing programs from scratch without code generators or code libraries. User verbs are a great way to reuse code. They are an important tool for many DB/C programmers. If you aren't using them already, you should start! There was one error in last month's discussion of file locks. The correct decimal value of 0x40000000 is 1073741824, not 107741824. don.wills@dbcsoftware.com ***************************************************************************** A Demonstration GUI File Maintenance Program In this article, we'll discuss the important details of a DB/C program that implements typical file maintenance functions. This program is too large to be included in this newsletter, but you can download it from the DB/C Software Company ftp server. If you want to retrieve this file with a web browser, use this URL: ftp://ftp.dbcsoftware.com/misc/guidemo/filemaint.prg If you are using an FTP client program, the file is "filemaint.prg" in the "/misc/guidemo" directory on the ftp.dbcsoftware.com server. Before getting into the details of the program, I'll discuss a couple of concepts that went into designing the GUI capabilities of DB/C. All GUI elements of DB/C adhere to the overriding design principle of the DB/C language - portability. We're very proud of how successfully we've fulfilled the goal of making DB/C portable. The most important GUI-only principle is what I call the dynamic principle. Many languages and development tools allow only static window design. That is, at design time, the programmer creates a static window layout that can't change at runtime. With the KEYIN and DISPLAY verbs, DB/C programmers have become accustomed to having much greater flexibility. In keeping with that, we designed the DB/C GUI operations so that all of the elements - windows, menus, dialogs, toolbars, etc. - can be created dynamically at runtime. Now for the details of "filemaint.prg". The sample program implements the typical file maintenance operations for a customer master file. The primary functions of the program are: Find, Next, Previous, Add, and Delete. We've also put in the menu items for printing and AIM searching, but they aren't implemented. The sample allows maintenance for a simple customer master file. This customer file contains these fields: customer number, name, address (two lines), city, state, zip, contact, phone, customer type, tax exempt status (Y/N), a list of up to ten catalogs associated with the customer, and a large comment field. To run the program, you need to create a DB/C type text file named "customer.txt". There is just one index on the customer number field. Thus, index the file "1-8" creating an index file named "customer.isi". "filemaint.prg" is then compiled with either the DB/C DX or DB/C JX compiler and then executed by the appropriate graphical runtime. At the top of the program, you will find two very large character variables. These contain the definitions of the menu and the layout of the controls in the interior of the window. In this example, these definitions are fixed at compile time. Note that these definitions could also be created dynamically at run-time by appending all of the elements together into a character variable. Next you'll see this lines: . window, menu and panel variables windev1 device mnures1 resource pnlres1 resource clipboard device These device and resource statements are variable definition statements, just like dim, form and ifile statements. Although the distinction is somewhat arbitrary, generally resource variables define things that are parts of a window. Device variables define windows and several other objects like timers and the system clipboard. Like other variable definition statements, these statements don't do anything. Verbs operate on them. Next is the primary queue variable definition: . main message queue variable msgs queue entries=256, size=256 A queue variable is a container for messages. You can think of a message as a record that never gets written to disk. The DB/C GUI programming model uses a queue to be the source of events caused by keyboard actions, mouse actions, and more. When actions occur, messages appear on the queue. The get verb is used to retrieve messages from a queue, much like the read verb is used to retrieve records from a file. Note that, in conjunction with the get verb, a queue variable provides messages to the program in same order that the events actually occurred. This provides a measure of asynchronous action to a GUI program. The next several statements define work variables and the customer file variables. Then the main window is created by this statement: prep windev1, "window=W1, size=480:320, title=Demo GUI Program" The window named W1 is created and displayed on the screen. It is 480 pixels wide by 320 pixels tall. Its title is "Demo GUI Program". Next the menu and panel resources are created with these statements: prep mnures1, menu1 prep pnlres1, panel1 Note that the second parameters are the names of the large character variables that were defined at the top of the program. Both of these resources will become parts of the main window in the next few statements. The link verb is used to make a logical connection between the queue variables and the window, the menu and the panel. These three link statements cause messages that originate from the window, menu and the controls in the panel to be stored in the queue variable named msgs. link windev1, msgs link mnures1, msgs link pnlres1, msgs The next few statements set various initial settings in the menu and panel before they are actually displayed in the window. The menu and panel are actually shown in the window with the following two statements: show mnures1, windev1 show pnlres1, windev1 At this point, the window is fully active and messages may start to be stored in the message queue. Before the program processes any messages, it needs to open the clipboard and see if there is any text in it. That's what these statements do: open clipboard, "clipboard" load work from clipboard if (work = "") set pasteflag endif Note that even if the clipboard has text on it, the Paste menu item isn't active yet because the customer file isn't open yet. The program lines that dispatch messages are next: . message dispatch msgloop wait msgs get msgs; msg chop msg.name to msg.name goto menu1msg if (msg.name = "M1") goto panel1msg if (msg.name = "P1") goto window1msg if (msg.name = "W1") goto msgloop The wait statement waits until there is at least one message in the queue. When there is, the wait statement finishes and the get statement executes. Then, based on the name of the device or resource that is the source of the message, program control is transferred to the appropriate code via a goto. The message handling for messages that originate from the window is simple. The only message we care about is the "CLOS" message. This message happens whenever the user clicks on the close box in the menu title bar or presses the key sequence that causes the same effect (ALT-F4 in Windows). The following lines accomplish this: . handle messages sent to window device window1msg goto quit if (msg.func = "CLOS") goto msgloop The processing is much more complex for the menu and panel resources. In general, messages from a menu cause a function to be called whereas messages from panel controls generally just change variables in the program to new values. Many messages cause the state of the program to change in such a way that the status of one or more menu items ("available" or "gray") needs to change. This is actually one of the most complicated parts of a good GUI program - making sure that those menu items that are irrelevant at any point in time are grayed out. There are several flags that control the menu items. The routine named "menusettings" alters the menu items. In doing its job, this routine tries to minimize the number of change operations that are done. The "ITEM" messages received by the routine that handles the panel messages are the source of the data that is stored in the customer record. "ITEM" messages are produced whenever the value in a control changes, except that listbox and scroll bar message handling is noticeably different. The displayfields routine causes the contents of a record to be displayed in the controls in the panel. This routine consists primarily of change verbs that change the values in each individual control. There are three functions named msgdialog, okdialog and fielddialog located at the end of the program. These functions are generalized dialog boxes used by several different parts of the program. The msgdialog function displays a dialog with a single line of text and one pushbutton named "Continue". The okdialog function displays a dialog with a single line of text and these two buttons: OK and Cancel. If OK is pressed, the over flag is cleared on return. Otherwise Cancel is pressed and the over flag is set on return. The fielddialog function displays a single line of text followed by a text edit control. It also contains an OK and a Cancel pushbutton. This program should serve as a starting point for your DB/C GUI program development. Bug fixes and comments are welcome! ***************************************************************************** Subscribing to the DB/C Newsletter If you don't already have the DB/C Newsletter delivered to your email address and would like to have it emailed to you when it is produced, just send an email message to 'request@dbcsoftware.com' and put the line 'subscribe dbcnews' in the body of the email message (omit the ' characters). The newsletter will be delivered to the email address from which the message was sent. To stop delivery, put the line 'unsubscribe dbcnews' in the body of the message.