Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

Examining the Zinc Interface Library


DEC90: EXAMINING THE ZINC INTERFACE LIBRARY

EXAMINING THE ZINC INTERFACE LIBRARY

High-powered windowing tools for Turbo C++

Gary Entsminger

Gary is a writer, programmer, and consultant, and can be reached at the Rocky Mountain Biological Lab, Crested Butte, CO 81224 and on CompuServe [71141,3006].


Object-oriented programming encourages the reuse of code, primarily through inheritance. A programmer can create classes of objects and then derive new classes from them. But a crucial and (perhaps surprisingly?) subtle aspect of inheritance is that programmers can derive new classes from abstract classes they didn't write. If these classes are general enough to allow themselves to be extended, they can be distributed in the form of extensible libraries, without including source code. Library users can use these classes without necessarily knowing their implementation details, and derive new classes from them.

A great idea, I believe, but one still in an incipient stage. Hybrid object-oriented languages such as C++ and Turbo Pascal, in particular, are still new enough to lack a good assembly of class libraries. Object-oriented thinking is substantially different, and requires fresh perspectives. Translating old C and Pascal code just won't do. So it isn't surprising that we're just beginning to see the first wave of commercial libraries that are truly object-oriented, and not just rehashes of structured ideas. In this article I'll examine one of these, an impressive interface library from Zinc Software.

The Zinc Interface Library (ZIL) is a C++ class library that you can use to construct a windowed graphics or text interface for your applications. The interface handles all input (keyboard, mouse, and other device events), creates and manages windows, and routes event information to the appropriate windows. Your applications "run" in these windows.

Browsing the Package

ZIL is conceptually smart and, when in graphics mode, is similar in appearance to Microsoft Windows or possibly the Next machine. While the people at Zinc have plainly stated that it is not a Windows look-alike, ZIL offers a professional-looking user interface without the overhead of a complete environment such as Windows. In text mode, ZIL mostly follows the SAA/CUA guidelines. ZIL is easy to use (once you learn to think in terms of events and messages), and offers an excellent alternative for C++ developers who want to develop graphics applications outside the Windows (and other GUI) environments.

The Library consists of seven main sections. The first section involves event mapping. In fact, the Zinc Interface Library design is an excellent example of an event-driven architecture. In such an architecture, the flow of a program is determined by an event -- a force outside the current process, window, or the computer itself.

Events are essentially anything that happen within the system: keyboard, mouse, and other device input; error messages; messages sent to and from objects and devices, and so on.

Related to event mapping is the event manager, which consists of a control unit for input devices and a storage unit for event information. The other sections of ZIL include a window manager (the control unit for any windows added to the screen display), a screen display, help system, error system, and palette mapping (for customizing displays).

Using ZIL, you develop applications that respond to messages triggered by events. You set the interface up (easily), and then set up your application to run in a ZIL window as a derived window object. Each user-activated event (keystroke, mouse, and so on) is routed to the appropriate window, which decides how it's supposed to handle the event.

Applications "running" under ZIL share a similar, general-purpose interface. You can use the interface without having to rewrite (or even know about) the implementation details of the interface. You can add windows, window objects (buttons, icons, and so on), and pop-up and pull-down menus, leaving the management of these objects to the event and window managers.

Each application that uses ZIL works similarly. An event manager looks for input from the keyboard, mouse, and so on, and routes it via an event queue to the window manager. The window manager then sends a message to the appropriate window (see Figure 1).

Because the event manager knows about various displays (CGA, VGA, EGA, Herc, MDA, 43-line, 50-line, and so on) it can determine the appropriate adaptor and display automatically. Thus, an application can be written generally, without concern for specific hardware. If it uses multiple display classes, any application you write can be abstract regarding its screen display. You use one set of source code to generate output for both graphics- and text-based environments.

The screen display controls all low-level screen ouput. The base class for the screen display is UI_DISPLAY. It has the ZIL-defined descendants: UI_DOS_BGI_DISPLAY and UI_DOS_ TEXT_DISPLAY, and support for snow checking on CGA monitors and IBM's Topview (which supports Microsoft Windows and Quarterdeck's Desqview environments). Additionally, you can derive new DISPLAY objects and incorporate them in an application as easily as you would ZIL-defined objects.

The ZIL Help System lets you provide context-sensitive help information for end users. You generate a help file using a ZIL utility called GENHELP, and then add the UI_HELP_SYSTEM class and its descendants to your application (more on this later).

The ZIL Error System lets you display error information to the end user in a similar manner. Simply add the UI_ERROR_SYSTEM and its descendants to your application; when an error occurs, information you specify will be presented to the user in another ZIL window.

The Palette Mapping class allows you to define color combinations for window objects with either global color palette mapping or individual object color palette mapping. Thus, any interface you develop can easily be customized by you or an end user.

Using the Library

To use (and extend) the library, you should create an instance of a window, add library- or user-defined window objects, and add the window to the window manager. Window objects are anything you associate with a window: Objects that compose windows (borders, titles, and scroll bars, for example), objects that specify windows (such as menus and buttons), and objects that are inside windows (such as fields, numbers, strings, and actions).

Incorporating the library into an application means you must construct a screen display and an event manager, add any input devices to the event manager, construct a window manager, and add any windows to the window manager.

To see how this works in practice, the code shown in Example 1 (page 70) initializes the Zinc Interface Library and adds one application window to the window manager. Note that window objects are added to a window dynamically using the overloaded + operator and new. The window, including any window object added to it, is similarly added to the window manager with *windowManager + window.

Example 1, by the way, is an example of how to use operator overloading. Any class derived from UIW_WINDOW inherits the + operator overload. So window objects are added to windows and windows are added to the window manager via the same + operator. The definition is shown in Example 2 (page 70).

Example 1: Initializing the Zinc Interface Library

  //Construct a graphics display if possible.
  UI_DISPLAY *display = new UI_DOS_BGI_DISPLAY;
  if (!display -> installed)
      // if this system can't handle graphics,
      // delete the graphics
      // display we just created with new.

  {
      delete display;
      // then create a text display.
      display = new UI_DOS_TEXT_DISPLAY;
  }
    // Construct an event manager.
  UI_EVENT MANAGER *eventManager =
     new UI_EVENT_MANAGER (100, display);
                             // 100 = MaxNoEvents
  *eventManager              // add devices by constructing
                             // new instances of device class.
     + new UI_BIOS KEYBOARD
     + new UI_MS_MOUSE
     + new UI_CURSOR;
    // Construct a window manager.
  UI_WINDOW_MANAGER *windowManager =
     new UI_WINDOW_MANAGER (display, eventManager):
    // Construct and add a window to the window manager.
  UIW_WINDOW *window1 = new_UIW WINDOW (1, 1, 50, 20, WOF_NO_FLAGS,
                                                   WOAF_NO_FLAGS);
  * window1 // add window objects by constructing instances
            // of the window object class.
      + new UIW_BORDER
      + new UIW_TITLE ("WIN1", WOF_JUSTIFY_CENTER);

Example 2: Inheriting an overloaded operator

  UI_WINDOW_MANAGER & operator + (void *object)
    { Add (UI_WINDOW_OBJECT *) object); return (*this); }

You can add functionality to a window by constructing a new window object and adding it to the window, as shown in Example 3 (page 70). Any window derived from the abstract UI_WINDOW_OBJECT class has the built-in ability to move, size, minimize, maximize, restore, and exit itself. So any window, regardless of its specific functionality, generally looks and behaves like any other.

Example 3: Adding functionality to a window using the

  + operator

  + new MY_OBJECT

Under the Hood

Once we've constructed the event and window managers and added windows (containing applications) to the system, we sit back and let ZIL do the work, essentially through an event loop. In other words, the ZIL event manager gets an event, interprets it and then sends the interpreted information via an event queue to the window manager. The window manager routes the information it gets from the queue to the appropriate window, which acts accordingly. The event manager then loops getting the next keyboard, mouse, or other device event until the window gets an exit request. A simple event loop is shown in Example 4 (page 70).

Example 4: A simple event loop

  int ccode;
    UI_EVENT event; // an event is an instance of class UI_EVENT
    do // loop: do-while event not equal to ESCAPE
    {
       // Get input from user.
        eventManager.  Get (event, Q_NORMAL);

       // If ESC message, exit.
        If (event .type == E_KEY && event .rawCode == ESCAPE)
            event .type = L_EXIT;

       // Send event information to the window manager
        ccode = windowManager .Event (event);
    } while (ccode != L_EXIT);

Besides the many basic window objects that describe and manipulate a window, ZIL includes many complex window objects such as prompts, pull-down and pop-up menus, icons, strings, dates, and data fields. To demonstrate window object usefulness, the code segment in Example 5 (page 71) adds database-like input fields to a window.

Example 5: Adding a database style field to a window

  // database/field window code
  // Construct a window databaselike window.

  + new UIW_PROMPT (38, 0, "#", WOF_NO_FLAGS)
  + new UIW_NUMBER (40, 0, 6, &recordNumber, "",
  NMF_NO_FLAGS, WOF_NO_ALLOCATE_DATA | WOF_NON_
                                     SELECTABLE)

  + new UIW_PROMPT (2, 1, "Name ......", WOF_NO_FLAGS)
  + new UIW_STRING (15, 1, 27, tmpRecord.name, 26,
    STF_NO_FLAGS, WOF_BORDER | WOF_NO_ALLOCATE_DATA)

  + new UIW_PROMPT (2, 2, "Address...", WOF_NO_FLAGS)
  + new UIW_STRING (15, 2, 27, tmpRecord.address1, 26,
    STF_NO_FLAGS, WOF_BORDER | WOF_NO_ALLOCATE_DATA)

  + new UIW_STRING (15, 3, 27, tmpRecord.address2, 26,
    STF_NO_FLAGS, WOF_BORDER | WOF_NO_ALLOCATE_DATA)

  + new UIW_PROMPT (2, 4, "Phone.....", WOF_NO_FLAGS)
  + new UIW_FORMATTED_STRING (15, 4, 27, tmpRecord.  phone,
    "LNNNLLNNNLCCCC", "(...) ...-....", WOF_BORDER
    | WOF_NO_ALLOCATE_DATA);

ZIL's UIW_NUMBER class is complete and handles the char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, float, and double data types. UIW_NUMBER also permits various formatting including decimal, scientific, and so on.

A Smart Line Editor

In this section, I'll develop a smart line editor that utilizes many of ZIL's features. The example demonstrates how to derive a new class of objects from existing ZIL classes, construct a data field and add it to the line editor, construct and display event and window managers, add a new instance of a derived class (the line editor) to the window manager, set up and run an event loop, and destruct everything created.

Listing One (page 101) presents the complete source (excluding ZIL header files) for the line editor. The new class derived from ZIL adds file I/O capability to a window. It consists of a constructor that opens and reads a file, a destructor which writes the edited text back to the file, and a buffer for holding characters. Therefore, all the action, including construction of event and window managers, the event loop, and so on, occurs during construction and destruction phases.

The key to how the line editor works lies in the built-in capabilities of UIW_STRING, the ZIL string class. Note, for instance, the *window1->line_buffer reference in Listing One. The line buffer is updated each time the line displayed on the screen is revised. When we construct the line editor in window 1, we pass the line buffer information contained in a file (in this case, MK.BAT). Then, when we destruct the line editor, the contents of line_buffer are written back out to MK.BAT.

Any class derived from UIW_STRING and connected to the window manager can automatically detect screen types and devices (such as a mouse), and possesses numerous editing abilities, including cursor and mouse movement, delete word, undo, redo, and so on. For completion's sake, Listing Two (page 102) shows the MAKE file (included with ZIL) used to compile with Turbo C++.

Adding Help

A complete interface must have some kind of user-friendly help system. ZIL makes it very easy to create and access context-sensitive or global help while your application is running. Each help context (a page or more) can be assigned to one or more windows or to the general help context. At runtime, the F1 help key displays the help information assigned to the current window. If that window has no help assignment, the general help information is displayed.

Context-sensitive help information is kept in a binary file generated by GENHELP, the ZIL-provided help generation utility. GENHELP generates the appropriate files for you. All you have to do is add the Help System to your application, and write the help files.

Listing Three (page 102) illustrates how to create a window containing a field editor and a help system. For the sake of simplicity, this example uses only one window. It would, however, be very easy to add several windows to the application by creating a new window and adding window objects to it.

Products Mentioned

Zinc Software 405 S. 100 East, Suite 201 Pleasant Grove, UT 84062 801-785-8900 $199.95 System Requirements: Turbo C++ 1.0 DOS 2.0 or later 640K RAM and a hard disk

Summing Up

The Zinc approach -- utilizing event-and window-management based entirely on sending messages to objects -- seems intuitively correct. The window objects included in the library are more than enough to get you thinking-in-objects, and as I've demonstrated, deriving and adding new objects to the system is straightforward.

Because the library is truly object-oriented, it can significantly add, not only to your code, but also to your understanding of object-oriented programming techniques. Much can be learned from the many good C++ source code examples included in the package. And even the manuals (a tutorial and a reference built around the class hierarchy) are a class act.

Event-driven applications built around windows and window objects are the wave of the future. C++ programmers will be impressed by Zinc's contribution to event-driven architectures. My advice: Wrap it up and check it out. ZIL is a classy interface library.

_EXAMINING THE ZINC INTERFACE LIBRARY_ by Gary Entsminger

[LISTING ONE]


// Line Editor example using Zinc Interface Library -- (c) Gary Entsminger

#include <ui_win.hpp>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// Derive a new class which adds File I/O to a window.
class Line_editor: public UIW_WINDOW
{
public :
   Line_editor();      // Constructor
  ~Line_editor();      // Destructor
   char * line_buffer[64];
 };  // end class Line_editor declaration

// Define Line_editor's constructor
Line_editor :: Line_editor() : UIW_WINDOW(2,2,70,5,
                       WOF_NO_FLAGS, WOAF_NO_FLAGS)
{
FILE *textfile; // pointer to file
char ln[64];    // local char array to hold lines read from file

   // Open file
   if ((textfile = fopen("mk.bat", "r")) == NULL)
   {
      printf("Error opening text file\n Aborting.");
      exit(0);
   }

   // Read first 64 characters of file
   fseek(textfile,SEEK_SET,0);
   fread(ln,64,1,textfile);

   // Close file
   fclose(textfile);

  strcpy(*line_buffer,ln);
}  // end Line_editor constructor

  //  Define Line_editor's destructor.
Line_editor:: ~Line_editor()
{
FILE *textfile; // pointer to file
char ln[64];  // char array to hold a line read from file

  // Open Make file (MK.BAT)
   if ((textfile = fopen("mk.bat", "w")) == NULL)
   {
      printf("Error opening text file. Aborting. \n");
      exit(0);
   }

// Write edited text to the file
strcpy(ln,*line_buffer);
fwrite(ln,64,1,textfile);

   // Close file
 fclose(textfile);

} // end destructor.

main()
{
// Construct the display, trying for graphics first.
UI_DISPLAY *display = new UI_DOS_BGI_DISPLAY;
if (!display->installed)
{
delete display;
display = new UI_DOS_TEXT_DISPLAY;
}

// Construct the event manager and add three devices to it.
UI_EVENT_MANAGER *eventManager =
  new UI_EVENT_MANAGER(100, display);

*eventManager
                  + new UI_BIOS_KEYBOARD
                  + new UI_MS_MOUSE
                  + new UI_CURSOR;

// Construct the window manager.
UI_WINDOW_MANAGER *windowManager =
   new UI_WINDOW_MANAGER(display, eventManager);

// Construct a new Line_editor in window 1.
Line_editor *window1 = new Line_editor();

// Construct a new string field for the Line_editor in window1.
UIW_STRING *stringfield = new UIW_STRING(1,1,64,
             *window1->line_buffer, 64,
             STF_NO_FLAGS, WOF_NO_ALLOCATE_DATA);

// Add the window objects to the line_editor in window 1.
*window1
+ new UIW_BORDER
+ new UIW_MAXIMIZE_BUTTON
+ new UIW_MINIMIZE_BUTTON
+ new UIW_SYSTEM_BUTTON
+ new UIW_TITLE("MAKE .BAT editor", WOF_JUSTIFY_CENTER)
+ stringfield;  // Add the string field we constructed to window1

// Add  window1 to the window manager.
*windowManager + window1;

// Loop for user response.
int ccode;
UI_EVENT event;

do
{
// Get input from the user.
eventManager->Get(event, Q_NORMAL);

// Send event information to the window manager.
ccode = windowManager->Event(event);
}
while (ccode != L_EXIT && ccode != S_NO_OBJECT);

// Manually decouple stringfield from window1. Destruct all the objects we
// constructed in the opposite order in which we created them.
*window1 - stringfield;
delete stringfield;
delete window1;
delete windowManager;
delete eventManager;
delete display;
} // end main.


<a name="0272_0012">
[LISTING TWO]

### Production
OPTIONS=-O -w -P

### Debugging
#OPTIONS =-v -w -P

### Zinc information
INCLUDE=..\include
LIB=..\lib
!if !$d(MODEL)
MODEL=1
!endif

.SWAP
.PATH.obj=$(MODEL)
.PATH.lib=$(LIB)
.cpp.exe :
        tcc $(OPTIONS) -m$(MODEL) -I$(INCLUDE) -L$(LIB)
{$< zil$(MODEL).lib graphics.lib



<a name="0272_0015">
[LISTING THREE]

#include <ui_win.hpp>
#include "editor.hlh"

main()
{
// Initialize the display, try for graphics first.
UI_DISPLAY *display = new UI_DOS_BGI_DISPLAY;
if (!display->installed)
{
   delete display;
   display = new UI_DOS_TEXT_DISPLAY;
}

// Initialize the event manager.
UI_EVENT_MANAGER *eventManager = new UI_EVENT_MANAGER(100, display);
*eventManager + new UI_BIOS_KEYBOARD + new UI_MS_MOUSE + new
UI_CURSOR;

// Initialize the window manager.
UI_WINDOW_MANAGER *windowManager =
   new UI_WINDOW_MANAGER(display, eventManager);

// Initialize the help window system.
_helpSystem = new UI_HELP_WINDOW_SYSTEM("notepad.hlp",
   windowManager, HELP_GENERAL);

// Initialize the error window system.
_errorSystem = new UI_ERROR_WINDOW_SYSTEM;

// Create a field editor window.
UIW_WINDOW *editor = new UIW_WINDOW(4, 5, 66, 12,
   WOF_NO_FLAGS, WOAF_NO_FLAGS);

// Add window objects to editor.
*editor
  + new UIW_BORDER
  + new UIW_MAXIMIZE_BUTTON
  + new UIW_MINIMIZE_BUTTON
  + new UIW_SYSTEM_BUTTON
  + new UIW_TITLE("FieldEditor", WOF_JUSTIFY_CENTER)

  + new UIW_PROMPT(2, 1, "To:", WOF_NO_FLAGS)
  + new UIW_STRING(6, 1, 15, "Dr. Dobbs folk", 40, STF_NO_FLAGS,
   WOF_BORDER)

  + new UIW_PROMPT(28, 1, "Date:", WOF_NO_FLAGS)
  + new UIW_DATE(35, 1, 20, &UI_DATE(), "",
      DTF_SYSTEM | DTF_ALPHA_MONTH, WOF_BORDER,
                NO_HELP_CONTEXT)

  + new UIW_PROMPT(2, 2, "Message:", WOF_NO_FLAGS)
  + new UIW_TEXT(2, 3, 60, 4, "", 1028, TXF_NO_FLAGS, WOF_BORDER);

// Add field editor to the window manager.
*windowManager + editor;

// Wait for user response.
int ccode;
UI_EVENT event;
do
{
   eventManager->Get(event, Q_NORMAL);
   ccode = windowManager->Event(event);
} while (ccode != L_EXIT && ccode != S_NO_OBJECT);

// Clean up.
  delete _helpSystem;
  delete _errorSystem;
  delete windowManager;
  delete eventManager;
  delete display;
}


// Contents of editor.hlh........
// This file was created by the GENHELP utility.

const int HELP_GENERAL = 1;   // General Help
const int HELP_EDITOR  = 2;   // Editor  Help









Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.