Sizing Up GUI Toolkits

Last month, Ray got some expert programmers to show what object-oriented application frameworks can do with a simple graphics application. This month, he applies the same approach to GUI Toolkits.


November 01, 1992
URL:http://www.drdobbs.com/web-development/sizing-up-gui-toolkits/184408869

Figure 1


Copyright © 1992, Dr. Dobb's Journal

Figure 1


Copyright © 1992, Dr. Dobb's Journal

Figure 5


Copyright © 1992, Dr. Dobb's Journal

Figure 2


Copyright © 1992, Dr. Dobb's Journal

Figure 3


Copyright © 1992, Dr. Dobb's Journal

Figure 4


Copyright © 1992, Dr. Dobb's Journal

Figure 5


Copyright © 1992, Dr. Dobb's Journal

NOV92: SIZING UP GUI TOOLKITS

SIZING UP GUI TOOLKITS

Multiplatform, multilanguage, and more!

This article contains the following executables: GUIXVT.ARC GUIISL.ARC GUIAUT.ARC GUIWNX.ARC

Ray Valdes

Ray is senior technical editor at DDJ. He can be reached at 76704,51 on CompuServe or at rayval@ well.sf.ca.us on the Internet.


This article picks up where we left off last month. In October, my article "Sizing Up Application Frameworks and Class Libraries" presented the rationale for comparing the "apples, oranges, and bananas" of application frameworks, class libraries, and GUI toolkits.

Last month's article described in detail the challenge that Dr. Dobb's posed to a number of vendors, so I'll only recap the highlights here. DDJ asked each tool vendor to implement the same graphics application using their toolkit. My previous article presented results using Borland's ObjectWindows Library (OWL), Inmark's zApp application framework, Island Systems' object-Menu, Liant's C++/Views, and Microsoft's Foundation Classes (MFC). This month showcases Autumn Hill's Menuet/CPP, Island Systems' graphics-Menu, WNDX Corp.'s WNDX, and XVT Software's XVT toolkit. Also mentioned here is DDJ's homegrown version, which was implemented on both the DOS and Windows platforms; see Figure 1.

The packages in this issue are a bit more diverse than those of October. The tools in the October issue were all object oriented, all C++, and almost all Windows based (although available for other platforms as well). This month's selection includes two multiplatform-based GUI toolkits in C (XVT and WNDX), one DOS-based class library in C++ (Menuet/CPP), and a DOS-based GUI toolkit for C and Pascal (Island's graphics-Menu).

Our varied toolkit choices reflect the situation many programmers find themselves in, in which a program must get done, and there's no pre-existing dogmatic preference for a methodology. All levels of abstraction above the API are fair game--from the ground-level approach of a graphics library (such as our DOS-based implementation in C, which uses Borland's BGI library), to the middle-level elevation of a GUI toolkit, to the higher altitudes of the application frameworks discussed in our last issue. The toolkit selection is meant to be representative rather than comprehensive. Our emphasis on concrete results rather than methodological dogma is validated by today's application marketplace, in which application packages implemented using a wide range of tools compete head-to-head for buyers. Users neither know nor care if you used C++ or assembler, as long as your software is ready, bug-free, and packs the necessary features.

Our goal is to provide you with information necessary for choosing between these various tools and technologies for program construction. The rationale is that conventional product reviews can only go so far. To properly evaluate a complex tool, it's essential for you to see what it can do in the hands of an expert programmer familiar with that toolkit. By examining the complete code of a nontrivial graphics application and comparing the code to similar implementations using other packages, you can gain insight available in no other way.

Many packages use lines of code as a metric to distinguish themselves from competitors. Such rudimentary measurements are useful, but can be misleading unless you examine the actual code in question to get a feeling for the density and texture of the source material. You can obtain both the executable and the full source code for each implementation from DDJ; see "Availability" on page 5.

Also available electronically is the complete program spec, along with the programmer's notes for each implementation. Briefly, the DDJ sample program, known as HWX Browser, allows for interactive viewing and selection of samples of digitized handwriting data. The data is basically a collection of vectors (one group per letter) that can be displayed with MoveTo(), LineTo(), or PolyLine() primitives. Our spec provided plenty of leeway for implementors to use in making trade-offs between interface design, program functionality, and ease of implementation. If nothing else, the results are fascinating from a user-interface design point of view. Figure 1 through Figure 5 show the different interpretations of our DDJ UI spec. Tables 1, 2, and 3 show which features were implemented, the implementation sizes in lines of code, and the size of the executables, respectively. The following sections discuss each of this month's implementations, in alphabetical order.

Table 1: Feature sets in the different implementations of the DDJ HWX Browser (missing features do not imply lack of support by product).

            Autumn Hill     DDJ    Island Systems  WNDX Corp.  XVT Software
             Menuet/CPP  with BGI   graphics-Menu     WNDX      XVT Toolkit

File-open
 dialog            x        --            x             x            x
Data window
 is
 resizable        --        --           --             x            x
Data window
 is
 scrollable       --        --           --             x            x
Access
 commands
 via menu          x        --            x             x            x
Access
 commands
 via
 toolbar
 or button         x        --           --            --            x
Show menu
 help in
 status
 pane             --        --           --            --           --
Show
 general
 help in
 help
 window           --        --           --            --            x
Select
 instance
 by
 pointing

 to cell          --        --           --             x            x
Select
 letter
 by
 pointing
 to cell          --        --           --             x           --
Select
 instance
 by
 keyboard         --         x            x            --           --
Select
 letter
 by
 keyboard         --         x            x            --            x
Select
 instance
 by
 scrollbars        x        --           --             x           --
Select
 letter
 by
scrollbars         x        --           --             x           --
Show all
 letters
 and
 instances        --        --            x             x           --
Multiple
 kinds of
 views of
 instance         --        --           --             x            x
Display
 custom
 sequence
 of letters        x        --           --            --
Letters in
 scrolling
 graphic
 list              x        --           --             x           --
Change
 line
 color
 of letter         x        --            x             x            x
Change
 background
 color of
 letter            x        --            x             x            x
Change
 color
 ensemble
 (palette)         x        --           --            --           --
Change
 line
 thickness
 of letter         x        --            x             x           --
Change
 scaling of
 letter           --        --           --             x            x
Print
 letter            x        --           --            --           --
MDI-style
 child
 windows          --        --           --             x            x
Tear-off
 menus            --        --           --            --           --

Table 2: Source-code size (in lines of code) of different implementations fo the DDJ HWX Browser.

  Lines  Menuet/  DDJ  graphics-  WNDX   XVT
          CPP            Menu
  ------------------------------------------

  CPP    1672     --      --       --     --
  HPP     142     --      --       --     --
  C       --     1474    2215     1565  2257
  H       --      239     278      270   924
  RC      --      --      --       --     --
  DEF     --      --      --       --     --
  Total  1814    1713    2493     1835  3181

Table 3: Size of executable files (in bytes) of different implementations of the DDJ HWX Browser.

  Autumn Hill Menuet/CPP        355,846
  DDJ with BGI                   55,216
  Island Systems graphics-Menu  192,496
  WNDX Corp. WNDX               383,840{*}
  XVT Software XVT Toolkit       49,152{*}

{*} The WNDX and XVT implementations on Microsoft Windows require the usual amount of support from the Windows environment (900K in runtime DLLs and 3-5 Mbytes of additional data and resources on disk). In addition, the XVT executable requires approximately 200K of proprietary DLL support at runtime, while the WNDX executable requires a proprietary runtime file (an RSC file) approximately 37K in size.

Autumn Hill's Menuet/CPP

Autumn Hill's Menuet/CPP is a GUI class library written in C++ for the DOS platform. It represents the next-generation version of an earlier package, Menuet, which was written in C (and which is also still available). Like the original, Menuet/CPP supports the usual range of CUA-compliant widgets or controls: windows, menus, buttons, scrolling lists, combo boxes, spin buttons, gauges, and so on. There are standard dialogs for file selection, color selection, alerts, and queries. Figure 2 shows Autumn Hill's implementation of our specification. Listing One (page 113) shows the code that creates the main window and the string window.

Unlike the original, Menuet/CPP is object-oriented in design. It also relies on C++ constructs such as operator overloading. For example, to attach controls to a window, you use the left-shift operator as in the following sequence: aWindow << aButton << anotherButton << aTextField. You also use the same syntax to send a message to a window: aWindow << someMessage. The compiler disambiguates between the two by means of the different argument types. While these coding puns may jar the sensibilities of longtime C programmers, they are considered elegant in the C++ world. Fortunately, those whose sensibilities are offended can choose Autumn Hill's companion C-based toolkit. Operator overloading is found in many other C++ frameworks, not just Menuet/CPP. For example, object-Menu uses the + operator to add a window to the event-responder queue, and to add a menu item to a menu.

One interesting class in Menuet/CPP is mQuilt which encapsulates the behavior of composite rectangles. As you add an overlapping rectangle to an mQuilt (a set of nonoverlapping rectangles), the class transforms the new addition into the equivalent set of nonoverlapping rectangles. This class is used by Menuet's window manager to compute the viewable and nonviewable regions created by overlapping windows on the display.

Unlike standard Menuet, which runs on both DOS and UNIX, at the moment Menuet/CPP is primarily for DOS, and there is a version that supports CIC's pen extensions to DOS (known as PenDOS).

Regarding the level of abstraction, Menuet/CPP is located somewhere between an application framework and a GUI class library. As defined in the October 1992 DDJ, an application framework is distinguished from a class library in that a framework facilitates reuse not just of pieces of code and user-interface components, but also of program design and overall structure. In addition, a full-fledged application framework provides all the general-purpose functionality common to most applications -- not just UI components, but also support for undoing commands, printing, debugging, memory management, and so on.

Unlike those application frameworks directly influenced by the MVC paradigm in Smalltalk-80, Menuet/CPP seems to have a unique ancestry. There are also signs of an X-Window influence, for example, the way in which the terms "window manager" and "widgets" are used.

Compared to graphics environments such as Microsoft Windows, Menuet/CPP does not provide the bottom-most layer of graphics-display primitives -- draw a line, a polygon, bitblt, and so on -- equivalent to the GDI layer in Windows. Rather, your program must link to a DOS-based graphics library such as Borland's BGI or one of the optimized third-party products such as FlashGraphics, Genus GX, or MetaWindow. These graphics libraries usually just handle visual displays rather than hardcopy devices. In order to implement printing in the DDJ sample application, Autumn Hill used a companion package, BabyDriver, which is a printer-interface library that supports over 300 different printers.

Finally, it's important to note that, unlike some other toolkits, Autumn Hill does not routinely license the source code to Menuet/CPP. It does, however, make it available under special contract.

Island Systems' graphics-Menu

Island's graphics-Menu is a DOS-based GUI toolkit that is the precursor to Island's object-Menu, the C++ DOS-based application framework covered in last month's article. Figure 3 illustrates how Island Systems implemented the specification. You can use graphics-Menu with either C or Pascal programs.

Like other GUI toolkits, graphics-Menu is at roughly the same level of abstraction as pre-3.x Microsoft Windows. That is, there are the usual menus, check lists, dialogs, buttons, icons, and so on -- without the Windows 3.x non-GUI, system-level enhancements such as VMM, OLE, and DDE.

Listing Two (page 113) shows the C code for the main function and for drawing the main window.

The UI components in graphics-Menu have a beveled, 3-D look, reminiscent of but not the same as the look of object-Menu. The graphics-Menu package includes two interactive resource-design tools, one for menus and the other for icons. The menu designer generates C or Pascal code (as opposed to binary resource files, as with Windows' RES format).

A big difference between graphics-Menu and other GUI toolkits is the extensive support for forms-based data entry. An interactive tool (Data Entry Designer) allows creation of forms with range checking, context-sensitive help, shortcut keys, field-specific pop-up menus, and dBase-style data-entry templates (picture strings).

As do other GUI toolkits, graphics-Menu requires the use of a graphics library: either that native to your compiler (Borland's BGI or Microsoft's graphics library) or one of the third-party libraries (Metagraphics MetaWindow or Genus GX).

One nice feature of graphics-Menu, for those programs that have to permanently inhabit real mode, is the automatic detection and use of expanded memory. One disadvantage compared to object-Menu, is that it doesn't provide support for 32-bit compilers, such as Metaware C/C++.

With regard to speed, Island's implementation of the DDJ sample application in C, using graphics-Menu, felt much faster and more responsive than last month's C++ implementation using object-Menu( also a real-mode program). Also, the C++ implementation's memory requirements were such that we could only run it with most of our usual TSRs unloaded. By contrast, this month's non-OOP version had no such obstacles. The performance of last month's object-Menu implementation can be improved by using a DOS extender or 32-bit compiler, or even a DOS memory manager such as QEMM. But if you don't have your heart set on using C++, it may just be simpler and easier to use graphics-Menu instead.

WNDX's WNDX

WNDX, from WNDX Corp., is a multiplatform GUI toolkit available for Windows, DOS, Motif, OpenLook, and the Macintosh. The DOS-based version of WNDX includes Metagraphics' MetaWindow as the underlying layer; this package is known as MetaWNDX. As Figure 4 shows, we used the Windows-hosted version of WNDX for this article.

More Details.

In the arena of portable C-based GUI toolkits, WNDX takes a middle ground between the close-to-the-natives approach of XVT and the completely virtual approach of Neuron Data's Open Interface. Each of these approaches has its trade-offs. Open Interface supposedly reconstructs almost all GUI elements (such as scroll-bars and buttons) from a small set of graphics primitives. This "thick layer" approach, among other things, allows your program to sport the look and feel of one platform, say the Macintosh, on another, such as Windows. On the "thin layer" side, XVT's Portability Toolkit tries to remain close to the underlying API; in some cases, the result is that certain native functionality available on a given platform (for example, the Macintosh's List Manager API) is not part of the abstract portable API. By contrast, WNDX provides its own implementations of common functions such as the file-open dialog, using where possible lower-level native elements such as scroll bars. This provides your programs with a UI consistent across all supported platforms but which may differ from the Windows API.

For example, in WNDX's implementation of the DDJ sample application, the WNDX file-selection dialog, while similar to the common file dialog in Windows, works a little differently--enough to disconcert or irritate the habituated user. (See the accompanying textbox "How Does it Drive?") There also appears to be a small performance penalty associated with this approach (on my 386/33). On the other hand, the payoff of WNDX's approach is shown by the sample application, which packs a lot of functionality using a modest amount of source. Using a UI component that is an extension of the Macintosh List Manager functionality, the WNDX implementation displays a spreadsheet-like matrix of cells, each of which contains a sample glyph. The relevant C code is shown in Listing Three (page 113). The user can select a cell and resize its width or depth; the corresponding column or row is scaled appropriately. By double-clicking on a cell, the user can open any number of windows to display individual characters. These windows can be resized to scale the characters also. An additional wrinkle to the implementation is that the initial dialog for selecting glyphs uses two scrolling lists of graphical objects (one horizontal and the other vertical).

XVT Software's XVT

XVT Software's Portability Toolkit makes its purpose quite clear from the start, by means of its plain-spoken name. Designed for multiplatform graphics applications, the XVT Portability Toolkit is currently available for Windows, Presentation Manager, Macintosh, X/Motif, X/OpenLook, and also in character-mode versions for DOS, OS/2, UNIX, and VMS. (Figure 5 shows the Windows version.) The range is so impressive you almost don't notice that there is no support for graphics-mode DOS. Presumably this is next on the list.

XVT was, for a long time, the only commercial multiplatform tool to support the Macintosh. This is now changing; Neuron Data and WNDX are in the game, with bigger players to follow (Symantec's Bedrock and Microsoft's Alar), perhaps over the next year.

Now at version 3.0, XVT has evolved over the last five years from a paper-thin portable layer of abstraction over multiple native APIs to a more full-featured but still efficient toolkit that better covers all the corners of an abstract GUI API -- for example, by adding a platform-independent resource language and enhanced support for printing, debugging, and text editing. The code in Listing Four (page 114) shows some of the principal event handlers for the browser window.

Unlike WNDX, which attempts to offer the best of each native platform on all platforms via emulation, XVT is less ambitious, opting for native functionality where possible. An example of the file-selection dialog has already been mentioned. Another example is the resource-definition language. XVT uses URL (Universal Resource Language) files to specify application resources such as menus and dialogs. You use the CURL utility to translate URL source text files into native resource scripts. These native resource files are then processed by native tools (such as the Windows resource compiler) to produce binary resource files that can be bound to your executable. By contrast, WNDX defines application resources using RSC files, text files in WNDX's own format that are processed at run time by your executable.

Even with XVT's recent enhancements, its level of abstraction remains at a middle level (roughly equivalent to pre-3.x Windows), compared to a class library or application framework. As Island Systems and Autumn Hill have done, XVT Software has added a C++ package to its product line. The XVT++ class library, in keeping with the company's approach, is a thin layer between a C-based GUI toolkit and a C++-based GUI application.

Conclusion

Bob Metcalfe, the inventor of Ethernet, said recently, "The operating system of the mid-to-late 1990s will be somebody's class library." If you append the words "or application framework" to that sentence, it sounds like a plausible prediction. But right now, the operating system of the early '90s seems to be somebody's GUI toolkit (namely, Microsoft Windows), competing with a number of other toolkits that provide what Microsoft left out -- among other things, portability, thrifty use of resources, and/or being able to run programs directly on DOS. If these qualities are important to your application, you can use the sample implementation here to help you choose among the alternatives.

How Does it Drive?

The challenge DDJ posed to GUI vendors resulted in some interesting lessons in practical UI design. You're no doubt familiar with the usual tenets of user-interface design--make the layout uncluttered, the functions transparent, usage consistent, state visible, feedback immediate, and so on. But set all this aside for the moment, and consider the question of how programs feel on the first test drive.

By launching the many different implementations of the same DDJ sample program for the first time, we've discovered a few simple rules of thumb to help with the fact that you never get a second chance to make a first impression. First, a little bit of color seems to go a long way in making that first impression favorable. Your subjective mileage may of course vary, but to my eyes a color background (for example, Liant's simple blue expanse in the last issue) conveys the feeling of a substantial program, much more than the empty white space which may be the only visible manifestation of thousands of lines of clever programming.

As you place your hands on the wheel (or mouse, as the case may be), how do the controls feel when they are moved? Silky smooth and directly connected to what's on the screen? Or more like manipulating an object with a pole through ten feet of water? Alas, this is not always something your application program can directly control, but often a result of (in) efficiencies in the low-level graphics library.

In buying a car, you can look at consumer reports all you want, but all too often when you get in and drive the thing, your gut makes the actual decision, bypassing the brain's careful deliberation. In the early years of Microsoft Windows, any number of benchmarks showed the same or better elapsed times for actions such as opening a file or drawing a line of text, compared to the Macintosh versions of the same program (PageMaker or Microsoft Word).

Yet anyone who actually used the programs on both platforms can tell you that the Windows version felt jerky and slow, even on a CPU twice as fast as that of the Mac. (The situation has now changed, as a result of much effort in optimizing the GDI.)

In the DDJ sample implementations, the controls that felt the smoothest to my hands were those in Autumn Hill's version. I don't know if this results from Menuet/CPP's use of the FlashGraphics library or whether it comes from clever programming at higher levels of the system, but it bears further investigation. At a minimum, I'd like to link the code with other graphics libraries that have a reputation for speed, such as Metagraphics MetaWindow.

A final realization from working with the many implementations is how irritating small discrepancies can be. For example, every implementation used a file-selection dialog, which worked basically the same way. However, there were tiny differences from one to the other, not always consciously perceived until later, that often contributed to an overall prickly feeling about the implementation.

For example, as a result of being drummed into my fingers by many Windows programs, I'm now accustomed to the convention that the escape key is equivalent to the Cancel button, and that the spacebar selects whatever button has the focus. I don't know whether these particular choices make sense in the realm of UI design theory. But when using a custom-made file dialog such as in WNDX, Autumn Hill, or Island, there are little pinpricks of frustration when these components don't work as expected. Moral: If your program is targeting a particular population of users, its worth spending time to nail down every last one of the UI conventions.

--R.V.




_SIZING UP GUI TOOLKITS_
by Ray Valdes

[LISTING ONE]


//=========AUTUMN HILL'S MENUET/CPP Excerpt=========================

mWindow * stview_window( mFont * sysfnt )
{
   mWindow *wn = new mWindow( "View String", 0, sysfnt,
                              560, 370, wBDRFIXED );
   wn->setstatus( wsMODAL|wsDESTROY, 1 );

   mRect r( 20, 145, 510, 320 );
   mWnCtlAperture *ap = new mWnCtlAperture( r );
   ap->getnodes()->rgn->setbrush( hwxbrush );
   ap->settask( draw_st_task );
   ap->setstatus( xPOSTDRAW, 1 );
   *wn << *ap;

   r.ymax = r.ymin - 10;
   r.ymin = r.ymax - 20;
   mWnCtlHScrollBar
   *hsb = new mWnCtlHScrollBar( r, butRIGHTRIGHT );
   hsb->settask( st_hsb_task );
   hsb->set( st_hsb_reading );
   *wn << *hsb;

   r.set( 520, 145, 540, 320 );
   mWnCtlVScrollBar
   *vsb = new mWnCtlVScrollBar( r, butDOWNDOWN );
   vsb->settask( st_vsb_task );
   vsb->set( st_vsb_reading );
   *wn << *vsb;

   r.set( 25, 60, 95, 90 );
   mWnCtlSpinBut
   *sp = new mWnCtlSpinBut( r, sysfnt, "Instance",
                           instance_selector );
   sp->settask( inst_st_task );
   *wn << *sp;

   r += mPoint( 146, 0 );
   sp = new mWnCtlSpinBut( r, sysfnt, "Scale (PC)",
                           scale_selector );
   sp->settask( scale_st_task );
   *wn << *sp;

   r += mPoint( 146, 0 );
   mWnCtlButton
   *bt = new mWnCtlButton( r, sysfnt, "Print" );
   bt->settask( print_task );
   *wn << *bt;

   r += mPoint( 146, 0 );
   bt = new mWnCtlButton( r, sysfnt, "Exit" );
   bt->settask( exit_task );
   *wn << *bt;

   r.set( 105, 25, 455, 45 );
   mWnCtlField
   *fi = new mWnCtlField( r, sysfnt, 0, 40 );
   fi->settask( st_fld_task );
   fi->put( view_str );
   CurHwxStrField = fi->get();
   *wn << *fi;

   return wn;
}

//---------------------------------------------------------//

// create alphabet view window

mWindow * alview_window( mFont * sysfnt )
{
   mWindow *wn = new mWindow( "View Alphabet", 0, sysfnt,
                              560, 350, wBDRFIXED );
   wn->setstatus( wsMODAL|wsDESTROY, 1 );

   mRect r( 20, 105, 510, 300 );
   mWnCtlAperture *ap = new mWnCtlAperture( r );
   ap->getnodes()->rgn->setbrush( hwxbrush );
   ap->settask( draw_al_task );
   ap->setstatus( xPOSTDRAW, 1 );
   *wn << *ap;

   r.ymax = r.ymin - 10;
   r.ymin = r.ymax - 20;
   mWnCtlHScrollBar
   *hsb = new mWnCtlHScrollBar( r, butRIGHTRIGHT );
   hsb->settask( al_hsb_task );
   hsb->set( al_hsb_reading );
   *wn << *hsb;

   r.set( 520, 105, 540, 300 );
   mWnCtlVScrollBar
   *vsb = new mWnCtlVScrollBar( r, butDOWNDOWN );
   vsb->settask( al_vsb_task );
   vsb->set( al_vsb_reading );
   *wn << *vsb;

   r.set( 25, 20, 95, 50 );
   mWnCtlSpinBut
   *sp = new mWnCtlSpinBut( r, sysfnt, "Instance",
                           instance_selector );
   sp->settask( inst_al_task );
   *wn << *sp;

   r += mPoint( 146, 0 );
   sp = new mWnCtlSpinBut( r, sysfnt, "Scale (PC)",
                           scale_selector );
   sp->settask( scale_al_task );
   *wn << *sp;

   r += mPoint( 146, 0 );
   mWnCtlButton
   *bt = new mWnCtlButton( r, sysfnt, "Print" );
   bt->settask( print_task );
   *wn << *bt;

   r += mPoint( 146, 0 );
   bt = new mWnCtlButton( r, sysfnt, "Exit" );
   bt->settask( exit_task );
   *wn << *bt;

   return wn;
}

//---------------------------------------------------------//

// create character view window

mWindow * chview_window( mFont * sysfnt )
{
   mWindow *wn = new mWindow( "View Character", 0, sysfnt,
                              530, 330, wBDRFIXED );
   wn->setstatus( wsMODAL|wsDESTROY, 1 );

   mRect r( 25, 25, 280, 280 );
   mWnCtlAperture *ap = new mWnCtlAperture( r );
   ap->getnodes()->rgn->setbrush( hwxbrush );
   ap->settask( draw_ch_task );
   ap->setstatus( xPOSTDRAW, 1 );
   *wn << *ap;

   r.set( 310, 210, 390, 250 );
   mWnCtlSpinBut
   *sp = new mWnCtlSpinBut( r, sysfnt, "ASCII Code",
                            ascii_code_selector );
   sp->settask( asc_ch_task );
   *wn << *sp;

   r -= mPoint( 0, 85 );
   sp = new mWnCtlSpinBut( r, sysfnt, "Instance",
                           instance_selector );
   sp->settask( inst_ch_task );
   *wn << *sp;

   r -= mPoint( 0, 85 );
   sp = new mWnCtlSpinBut( r, sysfnt, "Scale (PC)",
                           scale_selector );
   sp->settask( scale_ch_task );
   *wn << *sp;

   r.set( 425, 125, 490, 150 );
   mWnCtlButton
   *bt = new mWnCtlButton( r, sysfnt, "Print" );
   bt->settask( print_task );
   *wn << *bt;

   r -= mPoint( 0, 85 );
   bt = new mWnCtlButton( r, sysfnt, "Exit" );
   bt->settask( exit_task );
   *wn << *bt;

   return wn;
}

//---------------------------------------------------------//

// create "about" window

mWindow * about_window( mFont * sysfnt )
{
   mWindow *wn = new mWindow( "About HWX Browser", 0,
                              sysfnt, 350, 250, wBDRFIXED );
   wn->setstatus( wsMODAL|wsDESTROY, 1 );

   mWnCtlIcon *logoicon = new mWnCtlIcon( mPoint(20,130),
                                          60, 60, ahs_logo,
                                          rgnFLAT );
   *wn << *logoicon;

   mRect r = mRect( 145, 25, 215, 48 );
   mWnCtlButton
   *bt = new mWnCtlButton( r, sysfnt, "Exit" );
   bt->settask( exit_task );
   *wn << *bt;

   mWnCtlText
   *tx = new mWnCtlText( mPoint(135, 175), sysfnt,
                         "H W X   B r o w s e r" );
   *wn << *tx;
   tx = new mWnCtlText( mPoint(215, 145), sysfnt,
                         "by" );
   *wn << *tx;
   tx = new mWnCtlText( mPoint(125, 115), sysfnt,
                         "Autumn Hill Software, Inc." );
   *wn << *tx;
   tx = new mWnCtlText( mPoint(150, 100), sysfnt,
                         "1145 Ithaca Drive" );
   *wn << *tx;
   tx = new mWnCtlText( mPoint(132, 85), sysfnt,
                         "Boulder, Colorado 80303" );
   *wn << *tx;

   return wn;
}

//---------------------------------------------------------//

// create main application window and its menu system

mWindow * app_window( mFont * sysfnt )
{
   // create main window
   mRect  r = mGdMgr::getdisprect();
   int    w = r.delx() + 1;
   int    h = r.dely() + 1;
   mWindow *wn = new mWindow( "HWX Browser", &main_menu,
                              sysfnt, w, h, wBDRSIZABLE );

   // get window's main menu
   mWnCtlBarMenu *mm = wn->getwnmenu();

   // attach pulldowns
   mWnCtlBoxMenu *
   sm = new mWnCtlBoxMenu( sysfnt, &file_menu );
   sm->settask( file_task );
   mm->setpulldown( sm, 1 );

   sm = new mWnCtlBoxMenu( sysfnt, &view_menu );
   sm->settask( view_task );
   mm->setpulldown( sm, 2 );

   sm = new mWnCtlBoxMenu( sysfnt, &optn_menu );
   sm->settask( optn_task );
   mm->setpulldown( sm, 3 );

   return wn;
}

//---------------------------------------------------------//

// perform app initialization

void init_app( void )
{
   SetDefaultPalette( pSky );
   strcpy( hwxpath, getcurrentdir() );
   strcat( hwxpath, "\\*.DAT" );
   memset( hwxfile, 0, PATHSPECLENGTH );
   CurHwxTbl = 0;
   CurHwxChar = 0;
   CurHwxStr = 0;
   CurHwxStrLen = 0;
   CurHwxCharSet = 0;
   CurHwxAlphabet = 0;
}

//---------------------------------------------------------//

// perform app termination

void term_app( void )
{
}

//---------------------------------------------------------//

int main( int argc, char *argv[] )
{
   init_app();
   mWindowManager *WM = new mWindowManager;
   MainWn = app_window( &WM->systemfont() );
   *WM << *MainWn;
   WM->run();
   term_app();
   delete WM;
   printf( "Availble memory = %ld bytes\n", farcoreleft() );
   return 0;
}



[LISTING TWO]


//==========================ISLAND SYSTEMS GRAPHICS-MENU=============

int main(int argc, char *argv[])
{
    GM_init(argc, argv);

    InitMenus();
    PrepareW1( &HM );           // W1 what we call the main window
    userproc1 = DisplayTime;    // this procedure displays the time
    DrawW1();
    FreezeWin( &W1 );
    do {
         PollUser();
         DoW1();
    } while (err==err); /* forever */
    GM_close();
    return 0;
}
//------------------------------------------------draw W1
void  DrawW1( void )
{
    HideCursor();
    DrawWindow( &W1,
                true /*DoBevel*/, gSaveWindow /*DoSave*/, true /*HasHmenu*/ );
    setcolor(gBackColor);
    PaintRect (&W1.WorkingR);

    gDrawArea.Xmin = W1.WorkingR.Xmin+8;    // set inner drawing rectangle
    gDrawArea.Xmax = W1.WorkingR.Xmax-8;
    gDrawArea.Ymin = W1.WorkingR.Ymin;
    gDrawArea.Ymax = W1.WorkingR.Ymax;

    setScrollRange();

    DrawScrollRect( &(W1.SbarR) );
    DrawDragBoxV( &(W1.SbarR), &(W1.DragBoxR), W1.PcntVert, W1.PcntR );
    DrawScrollRect( &(W1.SbarB) );
    DrawDragBoxH( &(W1.SbarB), &(W1.DragBoxB), W1.PcntHorz, W1.PcntB );
    ShowCursor();
}
//--------------------------------------------------------draw sample
void drawSample(int xOffset,
    int yOffset, boolean holdposx, boolean holdposy)
{
    Rect tmpR;
    static int  xLastOffset=0, yLastOffset=0;

    if (holdposx==true) xOffset=xLastOffset;
    if (holdposy==true) yOffset=yLastOffset;

    HideCursor();
    tmpR.left = gDrawArea.Xmin;  tmpR.top   =gDrawArea.Ymin;
    tmpR.right= gDrawArea.Xmax;  tmpR.bottom=gDrawArea.Ymax;
    setcolor(gBackColor);

    PaintRect(&W1.WorkingR);

    setviewport( gDrawArea.Xmin, gDrawArea.Ymin,
                 gDrawArea.Xmax, gDrawArea.Ymax, 1 );

    setlinestyle(gLineStyle, 1, gThickness);

    if (gDrawWhat==INSTANCE_DRAW) //display one sample
    {
         omShowSetOfInstances(tmpR, gCurrChar, gCurrScale, LIGHTGRAY,
                         gDrawColor, (int)xOffset, (int)yOffset);
    }
    else if (gDrawWhat==ALPHABET_DRAW)  // display alphabet for a sample
    {
         omShowAlphabet( tmpR, gCurrSample-1, gCurrScale, LIGHTGRAY,
                      gDrawColor, (int)xOffset, (int)yOffset);
    }
    else
    {
         omShowAll( tmpR, gCurrScale, LIGHTGRAY, gDrawColor,
                        (int)xOffset, (int)yOffset);
    }
    setlinestyle(SOLID_LINE, 1, NORM_WIDTH);
    xLastOffset = xOffset;   yLastOffset = yOffset;
    setviewport( sR.Xmin, sR.Ymin, sR.Xmax, sR.Ymax, 1 );
    ShowCursor();
}
//-----------------------------------------------------display about box
int displayAbout( void )
{
    rect R;
    MoveTo(MidPt.X-(12*StringWidthX), MidPt.Y-(5*FontHeight)); /*screen ctr*/
    HideCursor();
    DrawTextRect(10,26,10,10,LIGHTGRAY,LIGHTRED,true,&R,&err);
    PenColor(BLUE);                                /*text color  */
    BackColor(LIGHTGRAY);                          /*same as fill color*/
    DrawStringLN("      graphics-Menu");
    DrawStringLN("     Island Systems");
    DrawStringLN("     (617) 273-0421");
    ShowCursor();
    WaitForUser();
    if (Button)
      WaitForNot(Button);
    PopRect(&err);
    return 0;
}


[LISTING THREE]



//============= WNDX CORP.'S WNDX Excerpt ================================

void HWB_BigBrowseList( int          lMessage ,
                       int        lSelect ,
                  WX_rect     *lRect ,
                  WX_point    *lCell ,
                  int         lType ,
                  void         *lData ,
                  int          lLen ,
                  LST_Ptr      lHandle )

  {
   WX_rect        R;
   lpList         pInstance;

    if ( lMessage == LST_DrawMsg )
      {
        WX_Mode( WX_REP );
        R = *lRect;
        WX_InsetRect( &R , 2 , 0 );
        R.top++;
      R.left++;

      pInstance = HWX_GetInstanceData( ( lpList * ) lHandle->userHandle , lCell->Y , lCell->X );

      HWB_RenderInstance( lHandle->window , pInstance , &R );

      WX_MoveTo( lRect->left , lRect->bottom );
      WX_LineTo( lRect->right , lRect->bottom );
      WX_LineTo( lRect->right , lRect->top );

      if ( lSelect & LST_Maybe )
        WX_FrameRect( lRect );
     }
  }

int   CloseBigBrowser(DLG_Ptr dp)
  {
   free(DLG_GetDp(dp, HWB_DRAWOPTIONS));
   return TRUE;
  }

int   HWB_BigBrowserView( struct WND_Record *dp , int itemNo, struct EVNT_Record *ev,
                  int action, int msg, void *data)
  {
    char      *fileName;
   WX_point   whichOne;

   if ( action == DLG_hadDoubleClick )
     {
       DLG_Get( dp , dlg_filename , NULL , &fileName );

      DLG_GetItemCopy( dp , itemNo , itmList_lastClick , sizeof( WX_point ) , &whichOne );

      HWB_Instance( (lpList*) DLG_GetDp(dp, HWB_CHARDATA),
                 fileName ,
                 DLG_GetDp(dp, HWB_DRAWOPTIONS),
                 whichOne.Y , whichOne.X );

      DLG_SetIItem( dp, itemNo, itm_highlight, FALSE );
     }
   return TRUE;
  }

DLG_Ptr   HWB_BigBrowser( lpList CharData[] , char *fileName , DrawOptions *draw_opt , int defChar )

  {
    DLG_Ptr      dp;
   LST_Handle   LH;
   WX_rect      R;
   DrawOptions   *drw_opt;
   WX_point   defCell;

   dp = DLG_New( dlg_title , fileName ,
              dlg_centered_xy , 215 , 215 ,
              dlg_visible , FALSE ,
              dlg_filename , fileName ,
              dlg_close, CloseBigBrowser,
              dlg_menuname , "Options" ,
              0 );

   DLG_SetDp( dp , HWB_CHARDATA , CharData );

   drw_opt = malloc(sizeof( DrawOptions ));
   *drw_opt = *draw_opt;
   DLG_SetDp(dp, HWB_DRAWOPTIONS, drw_opt);

   DLG_GetCopy( dp , dlg_content , sizeof( WX_rect ) , &R );

   DLG_AddItem( dp ,
             listItm , "test" ,
               itm_action , HWB_BigBrowserView ,
               itm_placement , 0 , 0 , 1000 , 1000 ,
               itmList_top , 0 ,
               itmList_left , 0 ,
               itmList_defWidth , 50 ,
               itmList_defHeight , 50 ,
               itmList_verticalScroll ,  TRUE ,
               itmList_horizontalScroll , TRUE ,
               itmList_ReframeCellV , TRUE ,
               itmList_ReframeCellH , TRUE ,
               itmList_active , TRUE ,
               itmList_DoDraw , TRUE ,
               itmList_OnlyOne , TRUE ,
               itmList_rows , 128 , /* the defined maximum number of
                                  characters */
               itmList_columns  , 20 ,
               0 );

   DLG_SetPpItem( dp , 1 , itmList_defProc , ( iFunc ) HWB_BigBrowseList );

   LH = ( LST_Handle ) DLG_GetDpItem( dp , 1 , itmList_handle );

   LH->userHandle = CharData;
   LST_CalcVisible( LH );

   if ( ( defCell.Y = defChar ) >= 0 )
     {
       defCell.X = 0;

      LST_MakeVisible( &defCell , TRUE , FALSE , LH );
     }

   DLG_Draw( dp );

   return dp;
  }



[LISTING FOUR]


//=======================XVT SOFTWARE'S XVT Excerpt =======================
PRIVATE void     BW_ev_register (BW_DATA *BW_data_p)
{
    EV *ev;
    int count = 0;
    ev = EV_create (BW_SIZE_EV);
    BW_data_p->ev = ev;
    EV_set_eh (ev, count++, E_CREATE,  0, BW_create_eh);
    EV_set_eh (ev, count++, E_FOCUS,   0, BW_focus_eh);
    EV_set_eh (ev, count++, E_UPDATE,  0, BW_update_eh);
    EV_set_eh (ev, count++, E_DESTROY, 0, BW_destroy_eh);
    EV_set_eh (ev, count++, E_COMMAND, M_FILE_OPEN,  BW_propagate_eh);
    EV_set_eh (ev, count++, E_COMMAND, M_FILE_QUIT,  BW_propagate_eh);
    EV_set_eh (ev, count++, E_COMMAND, M_FILE_PRINT,BW_command_file_print_eh);
    EV_set_eh (ev, count++, E_COMMAND, M_OPT_ATTRS, BW_command_opt_attrs_eh);
    EV_set_eh (ev, count++,E_CONTROL,BW_BUTTON_ZOOMIN,BW_control_zoomin_eh);
    EV_set_eh (ev, count++,E_CONTROL,BW_BUTTON_ZOOMOUT,BW_control_zoomout_eh);
    EV_set_eh (ev, count++,E_CONTROL,BW_EDIT_CHAR,    BW_control_editchar_eh);
}
//-----------------------------------------------------handle create event
PRIVATE long     BW_create_eh (WINDOW win, EVENT *ev_p)
{
    FILE_SPEC fs;
    FILE *file_p;
    char title[SZ_TITLE], path[SZ_PATHNAME];
    BW_DATA *BW_data_p = (BW_DATA*)get_app_data (win);
    lplpList hwx_data;

    fs = BW_data_p->fs;

    file_p = BW_data_p->file_p;

    // Set the window title using the data file name.
    dir_to_str (&(fs.dir), path, sizeof (path));
    sprintf (title, "Browser - %s\\%s", path, fs.name);
    set_title (win, title);

    if ((hwx_data = BW_load_data (win, file_p)) == NULL) {
        close_window (win);
        return (EV_CONSUMED);
    }
    BW_init (win, hwx_data, 'A');

    // Enable/disable BW-specific menu items.
    win_menu_enable (win, M_FILE_OPEN, TRUE);
    win_menu_enable (win, M_FILE_PRINT, TRUE);

    /* Clear the window, set the starting values of the controls
     * in the BW window, and create the 3 child windows for the
     * graphics output.
     */
    clear_window (win, (COLOR)get_value (NULL_WIN, ATTR_BACK_COLOR));
    update_window (win);

    BW_data_p->BWZ_win = BWZ_new (win, 'A', 0);
    BW_data_p->BWI_win = BWI_new (win, 'A', 0);

    BW_do_status_update (win);

    return (EV_CONSUMED);
}
//---------------------------------------------------handle focus event
PRIVATE long      BW_focus_eh (WINDOW win, EVENT *ev_p)
{
    BW_DATA *BW_data_p = (BW_DATA*)get_app_data (win);

    if ((ev_p->v.active == TRUE) &&
        (BW_data_p->init_flag == TRUE))
        set_front_window (get_ctl_window (win, BW_EDIT_CHAR));
    else
        BW_data_p->init_flag = TRUE;

    return (EV_CONSUMED);
}
//-----------------------------------------------------handle update event
PRIVATE long   BW_update_eh (WINDOW win, EVENT *ev_p)
{
    BW_paint (win);
    return (EV_CONSUMED);
}
//-----------------------------------------------------handle destroy event
PRIVATE long    BW_destroy_eh (WINDOW win, EVENT *ev_p)
{
    BW_DATA *BW_data_p = (BW_DATA*)get_app_data (win);

    /* Free the HWX data structure (if it exists), and then free the
     * BW data object itself.
     */
    BW_free_data (win);
    xvt_free ((char*)BW_data_p);

    return (EV_CONSUMED);
}
//-----------------------------------------------------handle propagate event
PRIVATE long  BW_propagate_eh (WINDOW win, EVENT *ev_p)
{
    return (EV_PROPAGATE);
}
//------------------------------------------------handle attribute set evt
PRIVATE long   BW_command_opt_attrs_eh (WINDOW win, EVENT *ev_p)
{
    AD_new (win);
    return (EV_CONSUMED);
}
//------------------------------------------------handle zoomin control evt
PRIVATE long    BW_control_zoomin_eh (WINDOW win, EVENT *ev_p)
{
    BW_DATA *BW_data_p = (BW_DATA*)get_app_data (win);

    enable_window (get_ctl_window (win, BW_BUTTON_ZOOMIN), FALSE);
    enable_window (get_ctl_window (win, BW_BUTTON_ZOOMOUT), TRUE);

    BWZ_do_zoom (BW_data_p->BWZ_win, BWZ_ZOOMIN_FACTOR);
    BW_do_status_update (win);

    return (EV_CONSUMED);
}
//----------------------------------------------handle zoomout control evt
PRIVATE long    BW_control_zoomout_eh (WINDOW win, EVENT *ev_p)
{
    BW_DATA *BW_data_p = (BW_DATA*)get_app_data (win);

    enable_window (get_ctl_window (win, BW_BUTTON_ZOOMIN), TRUE);
    enable_window (get_ctl_window (win, BW_BUTTON_ZOOMOUT), FALSE);


    BWZ_do_zoom (BW_data_p->BWZ_win, BWZ_ZOOMOUT_FACTOR);
    BW_do_status_update (win);

    return (EV_CONSUMED);
}











Copyright © 1992, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.