Noel is a software development consultant specializing in leading-edge technologies, including object-oriented programming, MS-Windows, and OS/2. He can be reached on CompuServe at: 76704,34.
Object-oriented programming (OOP) promises to provide developers with a wide range of benefits. Programs will be easier to write, easier to extend, and easier to make portable. CommonView is an object-oriented C++ library (currently bundled with Glockenspiel's first-rate C++ implementation for DOS and OS/2). It is among the first such packages designed to bring OOP to the masses of C programmers who work with (or are interested in working with) Microsoft Windows and OS/2 Presentation Manager.
As we will see, CommonView has the potential to achieve this goal, but the current incarnation is immature.
You say that you're all in favor of easier and more portable programming, but what is Common-View? CommonView is an object-oriented library that maps the functionality of graphical user interfaces (GUIs) onto a set of object classes.
Glockenspiel bundles CommonView with their enhanced implementation of C++ for OS/2, DOS, and Windows. The total package includes the C++ translators, standard C++ libraries, the CommonView libraries, and tutorial and sample programs. A "driver" program, which is similar to Microsoft's CL.EXE driver, makes the process of using the C++ package almost like that of using Microsoft C.
CommonView differs from most object-oriented programming languages (such as Smalltalk, ACTOR, and C_Talk) that derive all of the classes in a tree with a single-base class (object) at the root. CommonView's classes are divided into a few separate categories: windows and events, controls, and miscellaneous classes.
For most programmers, installing CommonView should be smooth. The CommonView installation program allows you to specify a separate directory for each type of file that the program installs. Unfortunately for me, however, I place headers, libraries, and executables on a different partition from the partition that contains sample code. The install program doesn't allow you to specify separate drives, so the installation of CommonView on my personal system was more involved. I had to install the different components into the directory paths where I wanted the components to end up. I used XCOPY to move directories from the installed disk to the disk where I wanted them to be, and then deleted the original directories.
I encountered only a few minor glitches (such as leaving a header for one of the sample applications in the wrong directory) during the process of installing CommonView.
There is, however, one major problem for DOS users. The current version of C++ for DOS is very demanding with respect to memory, and some of the sample applications will not compile on most DOS machines. To solve this problem, you can spend an extra $100 to buy an extended memory version of C++. As another solution, you can compile under OS/2 and then link under DOS, which is the route I took.
A small problem also arises if you use separate names for the different types of libraries. For example, I use SLIBCER, SLIBCEP, and SLIBCEW for the names of the small model libraries for DOS, OS/2, and Windows. The driver program isn't quite smart enough to use the libraries directly.
The modified .CMD and .BAT files that I use to build CardFile are listed in Figure 1. Most of the options should be familiar because they are the same options used by Microsoft C's CL driver. The -lY option tells CCXX to add the library XlibY to the linker's list. (X is the model size, and Y is the extension.) CCXX calls the resource compiler for you, so when you link under DOS, the Windows resource compiler is called, rather than the PM's resource compiler.
MK.BAT: -- The original batch file from Glockenspiel ccxx -c -Gw -Zp cardfile.cxx ccxx -c -Gw -Zp edit.cxx ccxx -c -Gw -Zp expose.cxx ccxx -c -Gw -Zp cardapp.cxx ccxx -c -Gw -Zp cardstor.cxx ccxx -c -Gw -Zp dlg.cxx ccxx -c -Gw -Zp menu.cxx ccxx -c -Gw -Zp misc.cxx ccxx -Gw -Zp /NOE *.OBJ -oCardFile CardFile.RC slibw.LIB CardFile.DEF COMPIT.CMD: A batch file to compile under OS/2 set cl= set INCLUDE=c:\pmsdk\include;c:\windows\include;c:\commonvu\include set ccxx=Lr -FPi -Gsw -Zp ccxx -c -Zp cardfile.cxx ccxx -c -Zp edit.cxx ccxx -c -Zp expose.cxx ccxx -c -Zp cardapp.cxx ccxx -c -Zp cardstor.cxx ccxx -c -Zp dlg.cxx ccxx -c -Zp menu.cxx ccxx -c -Zp misc.cxx LINKIT.BAT: A batch file to link under DOS with the Windows library set cl= set LIB=c:\pmsdk\lib;f:\winsdk\lib;c:\njb\lib set ccxx=Lr -Icew -FPi -Gsw -Zp ccxx /NOE *.OBJ -oCardFile CardFile.RC slibw.LIB /NOD:slibce CardFile.DEF
EventContext Window AppWindow TopAppWindow ChildAppWindow DialogWindow
This category contains the classes responsible for implementing event dispatching and windows. The ancestor class at the top of this hierarchy is EventContext, from which a single descendant, Window, is derived.
CommonView's EventContext class virtualizes event dispatching. The inheritable virtual functions are Dispatch and Default. Dispatch is an event router. Default is the default event handler, and is invoked when a default event handler isn't overridden, or when the dispatcher receives messages for which it doesn't have a handler.
Technically, other types of event-driven systems could be derived from EventContext, but none are provided in the current CommonView release. In fact, the CommonView documentation says, "Although it is possible, in theory, to derive from EventContext, it is not possible for the CommonView programmer to overload effectively its member functions. To do this would require an indepth knowledge of both CommonView's implementation and the underlying Event system."
One of the drawbacks of this limitation is that you cannot implement serial I/O with CommonView, unless you resort to the use of timer messages. The simple fact is that in MS-Windows there are no communications events to dispatch. Serial I/O is performed by using a modified event loop that polls both the comm driver and the message queue.
Furthermore, CommonView's implementation of look and feel differs from the implementation of look and feel that is represented by the MS-Windows and PM Application Style Guides. In order to implement MDI under Windows 2.x, you must play some poorly documented tricks with Windows, and work with messages not directly supported in CommonView. Compounding the problem is the fact that if you go to all the trouble to implement MDI under CommonView, you will code look and feel directly into your application. If you port your application to other environments you will be required to change look and feel, and thus losing much of the promised portability. Also, if Microsoft changes either the MDI specification or the way in which the specification is implemented (highly likely), you will have to change the application code. This should be the responsibility of the library.
If you decide to implement the Microsoft Style Guide suggestions in your application, you will want to create your own "appropriate look and feel" object subclasses based upon the CommonView classes, and then use them throughout your coding. This approach makes the process of moving your CommonView application to a new environment less problematic. A sample MDI application available from Microsoft upon request or for downloading from their CompuServe Forum. The sample application illustrates most of the nastier tricks that are necessary.
The Window class provides two things. First, it implements an Event system for windows. Second, it defines the methods for manipulating Window objects.
The first point is important to understand. Normal MS-Windows or PM programming involves the use of the "Window Procedure" or WndProc. In CommonView, WndProc is virtualized with the Window classes' event handlers. The event handlers contain the list of private entry points to which Event objects are sent in response to messages being received from the environment.
This process is performed by translating MS-Windows messages into CommonView Event objects, and then dispatching them to the correct handler in the relevant window.
The different subclasses of Event can be received by the different types of event handlers. The subclass represent mouse events, key events, scroll events, menu events, control events, and window status events.
The Window class implements approximately 30 event handlers that receive and act on these Event objects. Some representative event handlers are WindowInit, MouseButtonDn, Activate, Expose, VerticalScroll, and MenuSelect. Most CommonView programmers will spend the majority of their time writing event handlers. New types of Window subclasses are created by deriving a subclass and overriding the inherited event handlers with new event handlers, which implement the behaviors unique to the new class.
ChildAppWindow represents child windows. ChildAppWindows are owned by instances of TopAppWindow, the class that represents pop-up windows in MS-Windows. DialogWindow instances are modal dialogs. If you want to create modeless dialogs, build them by using the AppWindow subclasses and Controls.
All of the classes in the Control hierarchy (Figure 3) that come with CommonView should also be familiar to Windows and PM programmers. This hierarchy consists of many of the common controls that most programmers already use, and can easily be extended. It is important to understand that Controls do not do anything in the CommonView structure. Controls cause instances of ControlEvt, ScrollEvt, EditEvt, and so on to be generated and dispatched to event handlers that belong to windows. All of the work is performed in the event handlers. The constructors used to build the Control instances attach the Controls to DialogWindow or AppWindow subclass instances. Text controls do handle the text editing that is part of the representation.
Control ScrollBar HorizScrollBar VerticalScrollBar FixedIcon TextControl Edit SingleLineEdit MultiLineEdit FixedText ListBox Button PushButton ClickBox RadioButton
CommonView provides other classes such as cursors and bitmaps, although not in any particular hierarchy. The collection of classes is provided in Figure 4. Most of these classes should be familiar to Windows programmers and PM programmers because they represent things within those environments with which we are already acquainted. For example, Accel is a class for building and working with keyboard accelerators. BitMap is a class for working with bitmaps. ResString, on the other hand, may not be familiar. This class is CommonView's way to handle constant strings in Resource files. ResStrings can be useful for allowing localization of your program (that is, customize messages or switch languages). Pair simply provides an ordered pair of integers, but is still a useful class. For example, Range can specify the limits of a scrollbar, a Point might contain a screen location.
Accel Bitmap Brush Caret Color Cursor Font Icon Menu SysMenu MessageBox ErrorBox Pair Dimension Point Range Selection Pen Rectangle ResString
CommonView comes with, and makes extensive use of, a set of extremely useful classes that Glockenspiel designed and released to the public domain. These classes are Container and FreeStore, which make up CommonView's memory-management system.
Containers are a virtualization of various logical access methods. For example, CommonView provides Heaps, Rings, Stacks, and Tables. Containers represent the way that we think about locating and accessing our data. For example, Tables provide keyed access to data. They are implemented as Rings with key access, but could also be implemented with a B-tree. Containers do not handle the actual storage of the items -- that is where FreeStores come into play.
FreeStores are a virtualization of physical (rather than logical) access to data. They provide a virtual Heap construct, on top of which the Container classes are built. CommonView comes with three FreeStore classes for MS-Windows: LocalHeap, GlobalHeap, and disk-based Heap. The disk-based FreeStore has not been implemented for OS/2, although that will undoubtedly be corrected in a future release. The source for all of the Container and FreeStore classes is provided, so you can implement the disk-based FreeStore yourself if you need it.
Containers and FreeStores take data and provide you with handles. Each Container is typed, which means that you only store a single kind of data within the Container. Actually, however, this is not entirely true. As long as each item's class is derived from the class whose type the Container holds, it is OK to store instances of that class in the Container. Thus, if you create a Heap of Windows, you can store instances of any Window subclass within that Heap.
A third class, Lock, is related to Containers and FreeStores. Lock is used to access data contained within that Heap. A Lock is created with the handle of the data item, and ensures that the data is both available in memory and locked in place. When you are finished with the Lock, you destroy it, either explicitly or by letting it go out of scope.
The Lock class is primarily an OOP convenience. The actual locking and unlocking process is performed by the FreeStore.
Let's take a look at a simple WinApp from the CommonView package that simply allows us to freehand sketch with the mouse. DOODLE (Listing One) is about as basic a CommonView program as one might want to write.
DOODLE defines a new subclass of TopAppWindow. This new type of window, the DoodleWind, only overrides two of the inherited default event handlers: MouseButtonDn and MouseDrag. Notice that no constructor is defined for DoodleWind. The protected constructor inherited from TopAppWindow is sufficient.
When the mouse button is depressed, DoodleWind remembers the starting location for the freehand sketch. When the mouse is moved, DoodleWind moves to the last remembered location of the mouse, draws a line from that location to the current mouse location, and remembers the current mouse location for use the next time MouseDrag is invoked by the Event dispatcher.
That's it. In a few lines of code this WinApp creates a movable, resizable window with a system menu, and is capable of freehand drawing. We could extend it fairly easily. Let's say that we maintain a list of the mouse locations used in the drawing, perhaps by using a subclass of Ring -- one of CommonView's Container classes. We would override the default Expose handler to repaint the display, using the stored Points in our PointRing. We also override the default MenuCommand handler while adding a menu item, in order to erase the list of Points and start over. When MenuCommand handles the erase request, it should also use the RePaint method provided by Window in order to force a redisplay of the whole window -- MenuCommand should not erase the window itself.
MoveTo and LineTo are methods inherited from Window. LineTo, PaintRectangle, and TextPrint are essentially the only output methods that belong to Window, although there is control over the fonts, line types, and so on that the output methods would display.
Glockenspiel is developing a DrawObject hierarchy that would contain all of the things that could be displayed within windows. In the meantime, you can "kick down." This term means that you ask a CommonView object to give you its Window or PM handle, so that you can write code directly to the environment.
DrawObjects will respond to a Draw message and will be owned by Windows. Glockenspiel had intended to provide some samples, but pulled them from the disk at the last minute. A late summer release of CommonView is expected to include DrawObject, so by the time you read this article, some examples should be available to use as models for your own classes.
DOODLE also shows us the basic structure of a CommonView program. It contains a class, App. This class has a method, Start( ), that we need to provide. App::Start( ) is the CommonView equivalent of main( ).
App::Start( ) first creates a window by declaring an instance of DoodleWind. The system menu, a border, and a caption are added to the window. Finally, the window is made visible, and CommonView's dispatcher [Exec( )] is turned on.
This is the basic structure to the CommonView initialization and startup sequence. You will see this structure repeated in each CommonView application.
CommonView does make simple Windows and PM applications easier to use. The compiled applications are very fast and small due to the fact that CommonView is contained in a DDL (and, in fact, encourages the creation of other DDLS). Unfortunately, with CommonView's current limitations -- including the lack of printer support, EventContexts documentation, or graphics -- the initial release of the product may prove unusable for many programmers. The major upgrade expected in late summer 1989 is alleged to correct the vast majority of these problems, and is anxiously awaited by many CommonView pioneers.
All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063, or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number and format (MS-DOS, Macintosh, Kaypro).
CommonView, Version 1.2, ImageSoft Inc., 2 Haven Ave., Port Washington, NY 11050, 800-245-8840. System requirements: IBM PC AT or compatibles with at least 640K of RAM, Microsoft Windows 2.03 Software Developer's Kit (SDK) or Presentation Manager 1.0 SDK, Microsoft C 5.1 compiler. CommonView is bundled with Glockenspiel's C++ for $495.
_A First Look At CommonView_ by Noel J. Bergman
Copyright © 1989, Dr. Dobb's Journal
class DoodleWind : public TopAppWindow
long far MouseDrag ( MouseEvt );
long far MouseButtonDn ( MouseEvt );
void App::far Start()
Doodle.SetCaption ( "Doodle" );
long DoodleWind::far MouseButtonDn ( MouseEvt Evt )
LastPt = Evt.Where ();
long DoodleWind::far MouseDrag ( MouseEvt Evt )
MoveTo ( LastPt );
LineTo ( LastPt = Evt.Where ());
Copyright © 1989, Dr. Dobb's Journal