A Multicolumn List-Box Container for OS/2

Visual programming with GpfRexx

Brian Proffit

Brian was part of IBM's OS/2 development team. He is president of Visionary Research, the author of OS/2 Application Development Tools and OS/2 Inside & Out, and a contributing editor to OS/2 Magazine and OS/2 Developer. You can contact Brian on CompuServe at 75300,1466.


The basic list box has become a staple of graphical user interfaces because it provides a simple way of presenting scrollable lists of related information. Using a list-box control is much easier than writing code to paint the information in a panel, drawing the scroll bar, detecting user actions on the scroll bar, and repainting the list text accordingly. Unfortunately, standard list boxes are vector-based, with only one-dimensional information allowed.

Developers have looked for ways to take advantage of the coding ease of a list box, while being able to display two-dimensional information in a more tabular format. In short, they want a list box that can display multiple columns. Yet multicolumn list-box controls aren't routinely provided for in operating systems such as Windows and OS/2. In this article, I'll develop a multicolumn list-box control in a Workplace Shell container using the multiple-record list structure in Gpf Systems' GpfRexx.

Gpf is a C/C++ tool for creating GUIs for OS/2 Presentation Manager (PM) applications. REXX (REstructured eXtended eXecutor) is a high-level procedural language provided in OS/2 that uses English-like commands to implement program logic. For its part, GpfRexx is a visual programming environment that combines the power of REXX with the point-and-click simplicity of Gpf for rapidly designing, testing, and generating OS/2 PM GUI programs.

In addition to the full CUA '91 control set, GpfRexx also supports both standard OS/2 features (drag-and-drop, multithreading, and multitasking) and advanced ones (PM multimedia and DB2/2 SQL). More importantly, GpfRexx allows you to incorporate user-designed controls and custom bitmaps into programs, and to control display attributes such as colors, fonts, object placements, and the like--all without extensive knowledge of PM programming. For testing, GpfRexx (which generates royalty-free apps) provides its own multithreaded debugger.

While an interpretive environment like GpfRexx buys you rapid development (not to mention a lower learning curve than you face with C or C++), you may take a performance hit as your application grows. Luckily for those programs in which you must maximize performance, you can import applications created using GpfRexx into regular Gpf and generate native C or C++ code for OS/2 or Windows--without losing any of your original interface-design work.

I'll first use GpfRexx to create a multicolumn list-box control, then generate a C implementation of the control that you can later optimize. (Executable versions, data definitions, and related files for the control are available electronically; see "Availability," page 3.)

Creating a New Program

As with most visual-programming tools, the process of designing and creating new programs or utilities (such as the multicolumn list box) involves "filling out" multiple screens and selecting predefined functions. Figure 1 is the first GpfRexx screen you encounter. The Application Properties dialog box requires you to name the application, as well as define text that appears in the Task List describing the application. You also check radio buttons to determine if the resulting application is a stand-alone .EXE file or a smaller executable file that calls the separate GpfRexx DLLs.

Then you begin laying out the overall UI. As you would expect, GpfRexx generates a canvas on which to paint your interface. Standard default File and Help menus, which can be modified or removed, are included. The size and position of the window determines the starting size and position of your application's window. Double-clicking on the title bar brings up the Window Style dialog box that lets you change the text in the title bar and set user options such as sizing, minimizing/maximizing, and menus; see Figure 2. Changing the presentation parameters modifies the color and fonts used for objects in this window. The icon that represents this application is set here, and any entries you would like to add to the application's pop-up menu are defined as well.

Next, you select Create/Container to position and define the characteristics of the container object and actions you can perform on its contents from a Container Control dialog box. After closing this dialog, click on the container to select it, then size it to fill the main window.

Designing the Container

List-box entries are loaded as a series of records in a compound variable, often called a "stem." For the purposes of example, I'll fill the stem from prgrmrs.dat, a test file that contains a list of programmers, their current projects, and their percentages ahead of or behind schedule. The test file is a standard, comma-separated variable (CSV) file, with records laid out as follows: PROGRAMMER NAME, PROJECT TITLE, SCHEDULE PERCENTAGE (positive values indicate ahead of schedule).

To make the list box more visually indicative, I'll use different icons to identify those far behind (<= -10 percent), roughly on (-10 percent<x<10 percent), or far ahead (>10 percent) of schedule. You must define these icons within GpfRexx before they can be loaded into the stem. From the Create/Icon Object menu, select the filenames of the icons you want to add, and give them meaningful names for use in your program.

The key to this application is the use of a multiple-record list structure designed into GpfRexx as a predefined stem for use in a container. The necessary fields in the stem are shown in Figure 3. The container behaves much like the Drives object on the Workplace Shell desktop, with predefined Details, Icon, and Tree views, which present the information in three different ways. The Details view shows the information defined in the _Areas. Only the _Icon and _Title fields are displayed in the Icon and Tree views.

At this point, you need to create a user function to read the programmer file and load the list-box container. GpfRexx includes standard File Open and File Save As dialogs, as well as default functions to be executed when the application is initialized and when it is exited. Select Create/User Function Object to see the User Function Edit dialog box. This dialog displays a list of the primary objects already defined for the application, the methods available to act upon those objects, and the code associated with those methods. This simplifies copying existing code into your new function. In this case, however, I'll be creating all new code, so click on Zoom (so the entire window is available for our instructions), name the procedure CreateListBox, then move to the Multiple Line Edit (MLE) control and enter the instructions in Example 1.

Once the list-box stem is loaded, you need to load the field-information stem, again using the User Function Edit dialog box. Some tedious but uncomplicated coding is required to lay out the columns in the list box. I have introduced a second function (DefineFields) to compartmentalize this code for easier reading of the source, debugging, and maintenance. Figure 4 is the general layout of the field-information stem. Simply click on "add" in the User Function dialog box to create DefineFields with the code in Example 2.

Running the Program

That's essentially all that's required to build the multicolumn list-box control. However, you need to tell GpfRexx when you want CreateListBox to be executed. The most logical time would be when the main window is initialized. Returning to Figure 2, select the Action button to see the Action On dialog box, which lists the events that can take place on the selected object--in this case, the main window. You add CreateListBox to the window-initialization processing by simply pointing-and-clicking.

The last step is creating the executable program. Referring again to Figure 1, you select the Toolkit menu to have GpfRexx build a complete .EXE file ready to be run. Upon execution, the program reads the sample data file and displays the multicolumn list box in Figure 5.

Of course, the tabular view in Figure 5 isn't the only way to look at information when using containers. GpfRexx lets you, for instance, utilize the container's Icon View for a quick glance at data; see Figure 6. In this case, programmers more than 10 percent behind schedule are represented by a frown, those 10 percent or more ahead of schedule get a smiley face, and everyone else is in-between. The radio buttons I've added at the bottom of the display let you toggle between the two views using built-in GpfRexx functions.

REXX to C/C++ Conversion

Clearly, GpfRexx is a powerful tool for quickly building graphical programs. You can put together simple applications or prototypes in a fraction of the time needed in conventional development processes. But if your program grows so large that the speed disadvantage of an interpreted language such as REXX becomes a problem, you can import your GpfRexx application into the Gpf C/C++ toolkit and generate a C or C++ version. In other words, you can read an application-interface file created by GpfRexx directly into Gpf to move REXX programs to C.

All of the user-interface design work in the GpfRexx-created multicolumn list-box control moved over to Gpf without a hitch. The only necessary change was to rewrite the user functions in C. All of the hierarchy and navigation code translated automatically. The result is a Gpf 2.1 ORC file that is the same as the GpfRexx ORC, except that the REXX user-function objects are replaced with C user-function objects. C source for these user functions, a make to build them, and a user header that should be present when generating the application (for automatic inclusion in the generated source) are available electronically; see "Availability," page 3.

Translating the REXX is fairly obvious. GpfRexx provides functions for accessing PM objects and parameters. Most of these functions map directly onto PM APIs with only the type and number of parameters changing. This is because the calls have been simplified to insulate REXX programmers from the details of PM. In some cases, more work is required; for instance, GpfRexx provides functions to load list boxes or containers with an array or stem. This is not supported in C, so you must create a loop to sequentially insert or append items to the data in the control. The most difficult change in multicolumn list-box design involves loading data to a container control. In the user function that loads the list, you can see that not only is a loop required to load the data, but since it is a container, control memory has to be allocated to hold the data. Unlike a list box, a container holds pointers rather than strings.

As with GpfRexx, the process is basically point-and-click once you load Gpf and open the GpfRexx-generated file (PROGRMRS.ORC, in this case). Once you specify a C or C++ compiler, you'll see the same main window you created with GpfRexx--including the icons.

Figure 1 GpfRexx automatically creates a blank window into which you can place graphical objects.

Figure 2 The Window Styles dialog box allows you to modify the appearance of the window and its contents.

Figure 3: The fields in the predefined stem for use in a container.

Record.0            Number of records in list

For each record 1 through n:

Record.n._Attributes     (e.g., COLLAPSED, DROPONABLE, etc.)
Record.n._Title          User-defined title text
Record.n._Icon           Icon name
Record.n._Data           The user data record
Record.n._Area.0         Number of fields (x) in this record
Record.n._Area.1
 .
 .              Contents of each field
 .
Record.n._Area.x

Figure 4: General layout of the field-information stem.

FieldInfo.0             Number of fields

   For each field 1 through n:

FieldInfo.n._Title       Title text for this field
FieldInfo.n_Area         A number (x) corresponding to this field's place
                          number RecordInfo.n._Area.x in the multiple record
                          list stem
FieldInfo.n._DataStyle   Justification of data and column separator switch
FieldInfo.n._TitleStyle  Justification of title text

Figure 5 The multicolumn list box.

Figure 6 Utilizing the container's Icon View.

Example 1: Code needed to create a user function to read the programmer data file and load the list-box container.

/* Let user know this may take a second by changing cursor to an hourglass. */
SetPointer('SPTR_WAIT')
ProgrammerFile = 'prgrmrs.dat'

/* Initialize the record count in the first record */
ProgRecord.0 = 0

DO WHILE LINES(ProgrammerFile)   /* As long as there's more, */
  ProgData = LINEIN(ProgrammerFile)         /* read a record */

  /* Break the record into its component fields */
  Parse Var ProgData ProgName ',' Project ',' SchedPercent
  ProgRecord.0 = ProgRecord.0 + 1       /* increment counter */
  n = ProgRecord.0                     /* current record number */
  /* Set appropriate icon */
  SELECT
    WHEN SchedPercent<= -10 THEN ProgRecord.n._Icon = 'FaceFrown'
    WHEN SchedPercent>= 10 THEN ProgRecord.n._Icon = 'FaceSmile'
    OTHERWISE ProgRecord.n._Icon = 'FaceOkay'
    END  /* SELECT */

  /* Now load the data into the stem */
  ProgRecord.n._Title = ProgName   /* Show name in all views */
  ProgRecord.n._Data = ProgData
  ProgRecord.n._Area.0 = 3
  ProgRecord.n._Area.1 = ProgName
  ProgRecord.n._Area.2 = Project
  ProgRecord.n._Area.3 = SchedPercent
  END  /* DO WHILE LINES(ProgrammerFile) */
rc = STREAM(ProgrammerFile,'C','CLOSE')        /* close file */
/* Define the layout of the columns of the listbox           */
Perform('DefineFields');
/* Define the initial view in the container (just like the   */
/* DRIVES object on the desktop has Detail, Icon, Tree       */
SetCnrView('ContProgrammers','DETAILS')
/* Finally, load the ProgRecord stem into the container */
Parent = InsertCnrRecordList('ContProgrammers','ProgRecord.')
/* Note that the pointer is automatically reset */

Example 2: Loading the field-information stem.

ProgField.0 = 3         /* Set field counter */
DO Field = 1 TO 3
  ProgField.Field._Area = Field  /* Match fields to list areas */
  ProgField.Field._TitleStyle = "CENTER"      /* Center titles */
  END  /* DO Field */
/* Set parameters for Programmer Name */
ProgField.1._Title = 'Programmer'
ProgField.1._DataStyle = 'SEPARATOR'   /* Default left justify */
                          /* and specify vertical bar on right */
/* Set parameters for Project Title */
ProgField.2._Title = 'Project'
ProgField.2._DataStyle = 'SEPARATOR'

/* Set parameters for Schedule Percentage */
ProgField.3._Title = '% ahead of sched.'
ProgField.3._DataStyle = 'CENTER SEPARATOR'   /* Center column */
SetCnrFieldInfo('ContProgrammers','ProgField.')

For More Information

GpfRexx
Gpf Systems Inc.
30 Falls Road
Moodus, CT 06469-0414
800-831-0017
$247.50


Copyright © 1994, Dr. Dobb's Journal