Channels ▼


C Programming

Source Code Accompanies This Article. Download It Now.


This article contains the following executables: DFLAT5.ARC

This, the annual C issue, marks the beginning of my fourth year as Dr. Dobb's Journal's C Programming Columnist. Pardon me if I puff out my chest when I say that. Every month I get to write some C code, read some books, drag out my soapbox, and write for the leading programmer's magazine. All during each month I talk to other programmers on CompuServe. Every now and then I go some place such as Boston, New Orleans, or San Francisco for a computer show or a programming symposium. And they call this work.

This month we will repair some of the D-Flat files published in past columns, take a look at Power C, the bargain-basement ANSI C compiler, and discuss the latest C++ compilers for the PC. The discussion about C++ is a sneaky way for me to plug the second edition of my C++ book. It's traditional for computer columnists to use their columns to plug their own books. The practice is called the "Pournellian Imperative."

Fixing D-Flat

First, you jack up de car...

We're into the fourth installment of D-Flat, and already I have some corrections to make to code previously published. The three listings this month are new versions of dflat.h (Listing One, page 174), config.h (Listing Two, page 175), and window.c (Listing Three, page 177). These are some of the files that have changed. I will publish the others along with some new code next month. This time I'll explain what has changed in the system that affects existing code.

One of my complaints about many windows and user interface libraries is that small programs often must carry the weight of most of the library in the .EXE file, even though the program does not need most of the features. I found the same thing happening to D-Flat. As I added features to the user interface, the size of an applications program grew, even though the applications code had not changed. That was contrary to my original intentions for D-Flat, so I had to do something about it. I decided to let the C preprocessor be the program configuration manager. If an application does not need a D-Flat feature, you do not need to compile the code that supports the feature. Inasmuch as code to support discrete features might be found anywhere in the many D-Flat source files, the best way to suppress features is with compile-time conditionals.

Listing Two, config.h, contains some new global definitions. They are named INCLUDE_SYSTEM_MENUS, INCLUDE_DIALOG_BOXES, and so on. If you do not want system menus in your program, for example, you remove the INCLUDE_SYSTEM_MENUS global definition from config.h and recompile the D-Flat library. The effect will be that you will not be able to move, resize, minimize, and maximize the windows, but the program will be a lot smaller. A simple CUA program that uses none of the configurable features will be as much as 60K smaller than one that uses everything.

By selectively including and excluding the global definitions, you can generate or suppress system menus, the time-of-day clock display, the multiple document interface, scroll bars, shadows, dialog boxes, the clipboard, multiple-line listboxes and editboxes, and message logging. This approach means that D-Flat is not necessarily a static library that you use unchanged for all your projects. Instead, you modify the library functions to suit the needs of the specific application. This practice is consistent with my original design goals, which include using the compiler rather than additional runtime code to add window classes, menus, and dialog boxes to an application.

There are a number of other changes to window.c, which reflect problems I've had with clipping and repainting when the application has a lot of document windows. The new dflat.h and config.h files use the new INCLUDE_global symbols. dflat.h adds some members to the window structure to support features that I added to D-Flat in areas we haven't discussed yet but need more data in the structure. dflat.h has most of the prototypes and macros for the windows class methods, so they too have similarly changed and grown. Instead of specifying the names of the CLASS enum, dflat.h now includes a file named class.h, which you will see in a later column. This file localizes the definition of classes. A similar file named dflatmsg.h defines the message codes. I had to split these definitions into other header files because a new feature needs the class and message names in a displayable string format. I did not want to maintain redundant lists of classes and messages.

The new feature that uses strings of class and message names is a debug aid that logs selected D-Flat messages into a text file. A later installment describes the feature.

Some of the other source files have changed, and I am not going to republish them because the changes are minor and should have little effect on the operation of the program. Anyone who wants to use D-Flat should download it from CompuServe or TelePath. The latest version includes a sparse text file that documents the D-Flat functions, classes, and messages.

How to Get D-Flat Now

The complete source code package for D-Flat is on CompuServe in Library 0 of the DDJ Forum and on TelePath. Its name is something like DFLATn.ARC, where n is an integer that represents a loosely-assigned version number. Do not trust me to always tell you the correct name. The sysops might change its format. The D-Flat library that you download is a preliminary version of the package, but it works with most features in place. The help system is not there, however. I have not designed it yet. At present, everything compiles and works with Turbo C 2.0, Microsoft C 6.0, and Power C 2.0.2. There is a makefile for the TC and MSC compilers and a project file for Power C. There is one example program, the MEMOPAD program. Some readers have ported D-Flat to Watcom C and Zortech C++. They report few problems doing that. Read further to see what I ran into when porting D-Flat to Power C. If for some reason you cannot get to either online service, send me a formatted diskette -- any PC format -- and an addressed, stamped diskette mailer. A regular envelope works for 3.5-inch diskettes. Send it to me at Dr. Dobb's. I'll send you the latest copy of the library. The software is free, but if you care to, stick a dollar bill in the mailer. I'll give the dollar to the local food bank or other charity that takes care of homeless or hungry children, and you and I will feel good about it. The dollar is, of course, optional, as all charitable contributions should be. Call it "careware."

If you want to discuss D-Flat with me, my CompuServe ID is 71101, 1262, and I monitor the DDJ Forum daily.

Power C

If you are looking for a bargain in C compilers, check out Power C from MIX Software (Richardson, Tex.). For twenty bucks -- the typical cost of a C book -- you get a respectable book about C, but with an ANSI C compiler thrown in. Well, actually it's the other way around. The twenty bucks buys the compiler. Besides being the reference guide for the compiler and library, the user's manual is also a decent C language tutorial. So, take that twenty clams you budgeted for a book on C and buy a book and a compiler. But wait, there's more. For another ten bucks you can get the library source code. Add twenty bucks more for Power Ctrace, a source code debugger. MIX has other packages as well. A BCD math package and a database toolbox are twenty bucks each. These are garage sale prices.

I wanted to get D-Flat compiled with Power C in time for this issue. What better recession-bashing combination than a $20 compiler and a free CUA library? I uncovered a few twists in the way Power C does things, however, and I'll tell you about them here.

ANSI C allows you to call a function through a pointer without using the pointer dereferencing notation. The call looks like any other function call. Power C does not allow this convention if the pointer is in a structure. No problem; I put the parentheses and the asterisk into the call.

Power C does not allow you to initialize a structure with the assignment of another structure such as this:

  struct rect rc1 = rc2;

D-Flat does that all over the place, so I changed the initializations to assignments like this:

  struct rect rc1;
  rc1 = rc2;

The interrupt keyword in a Power C function pointer must be exactly the way Turbo C defined it and not the way that Microsoft C requires it. That required a compile-time conditional.

I had some problems with the move data function, replaced calls to it with a memcpy function call, and got past it. The Power C FP_SEG and FP_OFF macros are fashioned after the Microsoft C convention rather than the Turbo C convention, which D-Flat uses, so an adjustment fixed that.

At last the D-Flat example MEMOPAD program was loading and displaying its application window. The menus would pop up, but had no text in them, and the program did not properly load text files that I named on the command line. When I exited the program, DOS was dinged up. Time to fire up Power Ctrace, the MIX debugger.

First, I had to recompile everything with debugging turned on. After the compile, about 3 Mbytes of .TRC files were left on my disk. They are built by the compiler and used by the linker to generate a .SYM file for the Ctrace debugger. You can delete them after the compile, but be forewarned that you will need room for them while the compile is underway. I kept running out of disk space until I found out what was going on.

I run DOS 5.0 with everything in upper memory, and there were 605,920 bytes free. The MEMOPAD.EXE file compiled by Power C with debug information is 184,672 bytes long. The Power Ctrace program would not load the MEMOPAD program, saying that there was not enough memory. Earlier versions of DOS used a bunch of lower memory, and applications programs loaded well above the 64K boundary. Many programs, it turned out, would not run well if they started below 64K. I thought that Ctrace might be one of them, so I rebooted with DOS loaded low. Then there were a paltry 554,848 bytes available for applications. Ctrace still said there was not enough memory. If that ain't enough memory, Hoss, then Ctrace is one hungry memory hog.

A call to MIX's tech support revealed that Ctrace limits the symbol table to 64K. Apparently D-Flat has more symbols than will fit in the table. It's not that I don't have enough memory -- Ctrace just doesn't use enough of it. The MIX tech support person told me about a feature that disables the tracing of selected symbols, but to do that you need to get the program loaded in the first place, which I cannot do. The alternative is to compile most of the program without debugging information and compile the suspected modules with it, keeping the symbol table size down. A library such as D-Flat is an integrated whole. In a message-driven architecture with inherited window classes, every message passes through most of the code. A bug could be anywhere. In desperation, I reverted to the tried and true printf debugging technique, which uses printf calls in the code to tell you where you are going and what the variables look like.

The next thing I learned was that the Power C fnsplit and fnmerge functions do not work the way the Turbo C ones do. You pass the Turbo C functions NULL pointers to tell it not to split or merge a component of the file path and name. The Power C functions do damage to memory when you do that. You must pass pointers to zero-length strings to fnmerge and pass fnsplit pointers to character arrays that you are not going to use.

Power C emulates Microsoft C in some areas and Turbo C in others. Both of the big guys have unique extensions to the language and library to support the PC- and MS-DOS platform. Power C mixes the two sets of extensions. In some cases, the emulated extensions do not work exactly like the borrowed ones.

The last bug was tough, and it points to a serious departure from convention in the code compiled by Power C.

Power C casts far pointers to longs differently than the other compilers. The D-Flat message-passing protocols resemble Windows' parameter conventions in that generic long integers carry the parameters for the messages. To pass a pointer as an argument, I cast it to a PARAM, which is a typedef for a long. Power C doesn't like that cast, though. It converts the cast value into an integer that has the value of the pointer's offset, but does not include its segment. The most significant 16 bits of the cast value are zero. I had to change all those pointer casts to calls to a function that converts the pointer correctly to a long.

It took me about three hours to port the original D-Flat code from Turbo C to Microsoft C. I worked on the Power C port for three days before I finally got it working, primarily because I could not use the debugger. Although Power C is a good entry-level ANSI C compiler, I do not consider it to be serious competition as a software development tool because you must limit it to the development of small programs. However, there is no better bargain for the budget-minded student of C.

The State of C++

Last year I wrote a book called Teach Yourself C++, a C programmer's C++ tutorial with about 130 example programs. The code in the examples used C++ 2.0 conventions, and I used Zortech_C++ and a beta of Turbo C++ to get them running. Before the book went to press, I ran the exercises through Comeau C++ and Guidelines C++, which are ports of the AT&T CFRONT C++ translator program. Some of the examples compiled with Intek C++, a 386-only port of the CFRONT program, but 1.2 was the only version I had of that compiler.

Since the book came out, AT&T released its C++, Version 2.1 specification. In addition, I got a lot of letters with comments and questions about the book. To a writer, such events call for a second edition, which I have completed and the book should be in the stores by the time you read this column. The book is about generic C++, but because I use a 386 MS-DOS machine, I decided to borrow from the experience to report here on contemporary C++ compilers for the PC.

These are not reviews of the compilers. Although I used all of them to compile 130 small programs, that experience alone is not sufficient to allow me to compare their true performance. I will tell you about some problems I had, but these problems are not necessarily typical. They spring from my need to be as compatible with as many compilers as possible. C++ is not as well-defined or well-understood as C. You can expect the compilers to depart from one another and from what is thought to be the accepted definition. ANSI is working on a standard definition, but that will take a while.


Guidelines went out of the C++ compiler business, so there is no longer a Guidelines C++. Intek did not return my call, so I could not upgrade to their latest offering -- or even tell you if they still have one.

Borland C++ 2.0

Borland (Scotts Valley, Calif.) has repackaged their C++ with support for Windows 3 programming, but the compiler supports only the 2.0 specification. Borland C++ is a compiler implementation of C++ 2.0 that was originally packaged as Turbo C++ 1.0, but Borland has since modified their C++ to align their version number with the AT&T release and to add support for Windows programming.

I had no problems with Borland C++, primarily because I used a beta of the predecessor, Turbo C++, to develop the original 130 programs. The only exceptions were with the new features that C++ 2.1 added to the language. There are several exercises in the book that demonstrate 2.1 behavior and that do not compile with Borland C++ because the compiler is for version 2.0. That's not a problem; that's just how things are.

Comeau C++

Comeau C++ (Comeau Computing, Richmond Hill, N.Y.) upgraded to version 2.1 some time ago. Comeau C++ is a CFRONT port, which means that it is an adaptation of the AT&T C++ 2.1 translator. The compiler comes in versions that run under MS-DOS and Unix. The CFRONT program reads your C++ source code and translates it into C code that must be compiled by a C compiler. You will need a copy of Microsoft C to compile the translated output from Comeau C++.

The close association of Comeau C++ with the Microsoft C compiler caused some of my problems. The CFRONT program has a command line switch to indicate that the code uses ANSI conventions rather than K&R conventions. When you use that switch, CFRONT emits some code that Microsoft C does not compile. When you do not use the switch, the ANSI code does not pass the CFRONT syntax check. The code that CFRONT emits is correct ANSI C, but the Microsoft compiler doesn't accept it. This problem was in MSC prior to the formal acceptance of the ANSI C specification. Microsoft released MSC 6.0 after ANSI published the standard C definition, but Microsoft did not correct the problem.

If you want to use your PC to develop C++ code that you will port to a Unix CFRONT environment, or if you want to port some Unix C++ programs to MS-DOS, this is the compiler to get.

TopSpeed C++

TopSpeed C++ (Jensen and Partners, Mountain View, Calif.) is a compiler that implements C++, Version 2.1. The compiler is one part of a multiple-language product that includes C, C++, Pascal, and Modula-2. The languages share a common code generator and an integrated development environment. I found a few differences in the defaults for formatted stream output between TopSpeed C++ and the others. Nonetheless, most of the exercises compiled and ran without problems. I hit a few snags in the features that were in the C++ 2.0 specification and that 2.1 does not include. TopSpeed C++ does not have a _new_handler pointer that you can initialize, for example. It also does not permit the C++ syntax that creates anonymous, hidden variables such as when you initialize a reference with a constant.

Zortech C++

Zortech C++ (Zortech, Arlington, Mass.) is version 2.1, but the version number is their own, not an attempt to align with AT&T's 2.1. The compiler is closer to AT&T's 2.0 specification, although there are some differences even between AT&T 2.0 and Zortech 2.1. For example, Zortech's input/output stream classes and libraries are different. Rather than use the AT&T iostream library that all the other compilers use, Zortech decided to design their own improvements to the old C++ 1.2 iostreams. There is nothing wrong with that, I suppose, but I had to tell my readers to skip the entire chapter on iostreams if they use Zortech. It was either that or write a Zortech-specific chapter on iostreams, and I did not want to do that.

Undocumented DOS

If you write programs that extract the last ounce of performance from the MS-DOS platform, you've learned that you need more functions and internal data structures than the ones that Microsoft specifies in their Technical Reference Manual and in the officially-sanctioned books from Microsoft Press. There is a body of knowledge, collected in the underground and growing daily, that identifies the undocumented characteristics of DOS that you can use to do things that DOS does not support. The classic example is found in all the undocumented Mouseketeer machinations of the TSR memory-resident program.

Undocumented DOS (Addison-Wesley, Reading, Mass.), by Andrew Schulman, Raymond Michels, Jim Kyle, Tim Paterson, David Maxey, Ralf Brown, and the entire cast of "Hello, Dolly," is the programmer's equivalent of a Kitty Kelly unauthorized biography. (Celebrity trivia: Ms. Kelly was a drummer in a rock band in the '60s.) Undocumented DOS tells you all the good stuff about how Microsoft programmers get things to work -- when they do -- because they know what DOS's insides look like. Insider coding. This is an essential book for a PC programmer. There is a lot of C and assembly language code, and the book comes with a diskette that has all the code and the compiled programs. Undocumented DOS is the most informative DOS programming book I have read.

by Al Stevens

<a name="01e0_000f">

/* ------------- dflat.h ----------- */
#ifndef WINDOW_H
#define WINDOW_H

#define VERSION "Version 3 Beta"
#define TRUE 1
#define FALSE 0

#include "system.h"
#include "config.h"
#include "rect.h"
#include "menu.h"
#include "keys.h"
#include "commands.h"
#include "config.h"
#include "dialbox.h"

/* ------ integer type for message parameters ----- */
typedef long PARAM;
#define TE(m) m
typedef enum window_class    {
#include "classes.h"
typedef struct window {
    CLASS class;           /* window class                  */
    char *title;           /* window title                  */
    struct window *parent; /* parent window                 */
    int (*wndproc)
        (struct window *, enum messages, PARAM, PARAM);
    /* ---------------- window dimensions ----------------- */
    RECT rc;               /* window coordinates (0/0 to 79/24)  */
    int ht, wd;            /* window height and width       */
    RECT RestoredRC;       /* restored condition rect       */
    /* -------------- linked list pointers ---------------- */
    struct window *next;        /* next window on screen    */
    struct window *prev;        /* previous window on screen*/
    struct window *nextbuilt;   /* next window built        */
    struct window *prevbuilt;   /* previous window built    */
    int attrib;                 /* Window attributes        */
    char *videosave;            /* video save buffer        */
    int condition;              /* Restored, Maximized, Minimized */
    int restored_attrib;        /* attributes when restored */
    void *extension;            /* -> menus, dialog box, etc*/
    struct window *PrevMouse;
    struct window *PrevKeyboard;
    /* ----------------- text box fields ------------------ */
    int wlines;     /* number of lines of text              */
    int wtop;       /* text line that is on the top display */
    char *text;     /* window text                          */
    int textlen;    /* text length                          */
    int wleft;      /* left position in window viewport     */
    int textwidth;  /* width of longest line in textbox     */
    int BlkBegLine; /* beginning line of marked block       */
    int BlkBegCol;  /* beginning column of marked block     */
    int BlkEndLine; /* ending line of marked block          */
    int BlkEndCol;  /* ending column of marked block        */
    int HScrollBox; /* position of horizontal scroll box    */
    int VScrollBox; /* position of vertical scroll box      */
    /* ----------------- list box fields ------------------ */
    int selection;  /* current selection                    */
    int AddMode;    /* adding extended selections mode      */
    int AnchorPoint;/* anchor point for extended selections */
    int SelectCount;/* count of selected items              */
    /* ----------------- edit box fields ------------------ */
    int CurrCol;    /* Current column                       */
    int CurrLine;   /* Current line                         */
    int WndRow;     /* Current window row                   */
    int TextChanged; /* TRUE if text has changed            */
    char *DeletedText; /* for undo                          */
    int DeletedLength; /*  "   "                            */
    /* ---------------- dialog box fields ----------------- */
    struct window *dFocus; /* control that has the focus    */
    int ReturnCode;        /* return code from a dialog box */
#include "message.h"
#include "classdef.h"
#include "video.h"
enum Condition     {
void MessageLog(WINDOW);
/* ------- window methods ----------- */
#define WindowHeight(w)      ((w)->ht)
#define WindowWidth(w)       ((w)->wd)
#define BorderAdj(w)         (TestAttribute(w,HASBORDER)?1:0)
#define TopBorderAdj(w)      ((TestAttribute(w,TITLEBAR) &&   \
                              TestAttribute(w,HASMENUBAR)) ?  \
                              2 : (TestAttribute(w,TITLEBAR | \
                              HASMENUBAR | HASBORDER) ? 1 : 0))
#define ClientWidth(w)       (WindowWidth(w)-BorderAdj(w)*2)
#define ClientHeight(w)      (WindowHeight(w)-TopBorderAdj(w)-\
#define WindowRect(w)        ((w)->rc)
#define GetTop(w)            (RectTop(WindowRect(w)))
#define GetBottom(w)         (RectBottom(WindowRect(w)))
#define GetLeft(w)           (RectLeft(WindowRect(w)))
#define GetRight(w)          (RectRight(WindowRect(w)))
#define GetClientTop(w)      (GetTop(w)+TopBorderAdj(w))
#define GetClientBottom(w)   (GetBottom(w)-BorderAdj(w))
#define GetClientLeft(w)     (GetLeft(w)+BorderAdj(w))
#define GetClientRight(w)    (GetRight(w)-BorderAdj(w))
#define GetParent(w)         ((w)->parent)
#define GetTitle(w)          ((w)->title)
#define NextWindow(w)        ((w)->next)
#define PrevWindow(w)        ((w)->prev)
#define NextWindowBuilt(w)   ((w)->nextbuilt)
#define PrevWindowBuilt(w)   ((w)->prevbuilt)
#define GetClass(w)          ((w)->class)
#define GetAttribute(w)      ((w)->attrib)
#define AddAttribute(w,a)    (GetAttribute(w) |= a)
#define ClearAttribute(w,a)  (GetAttribute(w) &= ~(a))
#define TestAttribute(w,a)   (GetAttribute(w) & (a))
#define isVisible(w)         (GetAttribute(w) & VISIBLE)
#define SetVisible(w)        (GetAttribute(w) |= VISIBLE)
#define ClearVisible(w)      (GetAttribute(w) &= ~VISIBLE)
#define gotoxy(w,x,y) cursor(w->rc.lf+(x)+1,w->
WINDOW CreateWindow(CLASS,char *,int,int,int,int,void*,WINDOW,
       int (*)(struct window *,enum messages,PARAM,PARAM),int);
void AddTitle(WINDOW, char *);
void InsertTitle(WINDOW, char *);
void DisplayTitle(WINDOW, RECT *);
void RepaintBorder(WINDOW, RECT *);
void ClearWindow(WINDOW, RECT *, int);
void clipline(WINDOW, int, char *);
#define clipline(w,x,c) /**/
void writeline(WINDOW, char *, int, int, int);
void writefull(WINDOW, char *, int);
void SetNextFocus(WINDOW,int);
void SetPrevFocus(WINDOW,int);
void PutWindowChar(WINDOW, int, int, int);
void GetVideoBuffer(WINDOW);
void RestoreVideoBuffer(WINDOW);
void CreatePath(char *, char *, int, int);
int LineLength(char *);
RECT AdjustRectangle(WINDOW, RECT);
#define DisplayBorder(wnd) RepaintBorder(wnd, NULL)
#define DefaultWndProc(wnd,msg,p1,p2)    \
#define BaseWndProc(class,wnd,msg,p1,p2)    \
#define NULLWND ((WINDOW) 0)
struct LinkedList    {
    WINDOW FirstWindow;
    WINDOW LastWindow;
extern struct LinkedList Focus;
extern struct LinkedList Built;
extern WINDOW inFocus;
extern WINDOW CaptureMouse;
extern WINDOW CaptureKeyboard;
extern int foreground, background;
extern int WindowMoving;
extern int WindowSizing;
extern int TextMarking;
extern char *Clipboard;
extern WINDOW SystemMenuWnd;
/* --------------- border characters ------------- */
#define FOCUS_NW       '\xc9'
#define FOCUS_NE       '\xbb'
#define FOCUS_SE       '\xbc'
#define FOCUS_SW       '\xc8'
#define FOCUS_SIDE     '\xba'
#define FOCUS_LINE     '\xcd'
#define NW             '\xda'
#define NE             '\xbf'
#define SE             '\xd9'
#define SW             '\xc0'
#define SIDE           '\xb3'
#define LINE           '\xc4'
#define LEDGE          '\xc3'
#define REDGE          '\xb4'
/* ------------- scroll bar characters ------------ */
#define UPSCROLLBOX    '\x1e'
#define DOWNSCROLLBOX  '\x1f'
#define LEFTSCROLLBOX  '\x11'
#define RIGHTSCROLLBOX '\x10'
#define SCROLLBARCHAR  176
#define SCROLLBOXCHAR  178
#define CHECKMARK      251      /* menu item toggle         */
/* ----------------- title bar characters ----------------- */
#define CONTROLBOXCHAR '\xf0'
#define MAXPOINTER     24      /* maximize token            */
#define MINPOINTER     25      /* minimize token            */
#define RESTOREPOINTER 18      /* restore token             */
/* --------------- text control characters ---------------- */
#define APPLCHAR     176    /* fills application window     */
#define SHORTCUTCHAR '~'    /* prefix: shortcut key display */
#define CHANGECOLOR  174    /* prefix to change colors      */
#define RESETCOLOR   175    /* reset colors to default      */
#define LISTSELECTOR   4    /* selected list box entry      */
/* ---- standard window message processing prototypes ----- */
int ApplicationProc(WINDOW, MESSAGE, PARAM, PARAM);
/* ------------- normal box prototypes ------------- */
int isWindow(WINDOW);
WINDOW inWindow(int, int);
int WndForeground(WINDOW);
int WndBackground(WINDOW);
int FrameForeground(WINDOW);
int FrameBackground(WINDOW);
int SelectForeground(WINDOW);
int SelectBackground(WINDOW);
void SetStandardColor(WINDOW);
void SetReverseColor(WINDOW);
void SetClassColors(CLASS);
#define HitControlBox(wnd, p1, p2)     \
    (TestAttribute(wnd, TITLEBAR)   && \
     TestAttribute(wnd, CONTROLBOX) && \
     p1 == 2 && p2 == 0)
/* -------- text box prototypes ---------- */
#define TextLine(wnd, sel) \
      (wnd->text + *((int *)(wnd->extension) + sel))
void WriteTextLine(WINDOW, RECT *, int, int);
void SetAnchor(WINDOW, int, int);
#define BlockMarked(wnd) (  wnd->BlkBegLine ||    \
                            wnd->BlkEndLine ||    \
                            wnd->BlkBegCol  ||    \
#define ClearBlock(wnd) wnd->BlkBegLine = wnd->BlkEndLine =  \
                        wnd->BlkBegCol  = wnd->BlkEndCol = 0;
#define GetText(w)        ((w)->text)
void ClearTextPointers(WINDOW);
void BuildTextPointers(WINDOW);
/* --------- menu prototypes ---------- */
int CopyCommand(char *, char *, int, int);
void PrepOptionsMenu(void *, struct Menu *);
void PrepEditMenu(void *, struct Menu *);
void PrepWindowMenu(void *, struct Menu *);
void BuildSystemMenu(WINDOW);
int isActive(MENU *, int);
void ActivateCommand(MENU *,int);
void DeactivateCommand(MENU *,int);
int GetCommandToggle(MENU *,int);
void SetCommandToggle(MENU *,int);
void ClearCommandToggle(MENU *,int);
void InvertCommandToggle(MENU *,int);
/* ------------- list box prototypes -------------- */
int ItemSelected(WINDOW, int);
/* ------------- edit box prototypes ----------- */
#define isMultiLine(wnd)     TestAttribute(wnd, MULTILINE)
#define isMultiLine(wnd)     FALSE
/* --------- message box prototypes -------- */
void MessageBox(char *, char *);
void ErrorMessage(char *);
int TestErrorMessage(char *);
int YesNoBox(char *);
int MsgHeight(char *);
int MsgWidth(char *);

/* ------------- dialog box prototypes -------------- */
int DialogBox(WINDOW,DBOX *,int(*)(struct window *,enum messages,PARAM,PARAM));
int DlgOpenFile(char *, char *);
int DlgSaveAs(char *);
void GetDlgListText(WINDOW, char *, enum commands);
int DlgDirList(WINDOW, char *, enum commands, enum commands, unsigned);
int RadioButtonSetting(DBOX *, enum commands);
void PushRadioButton(DBOX *, enum commands);
void PutItemText(WINDOW, enum commands, char *);
void GetItemText(WINDOW, enum commands, char *, int);
void SetCheckBox(DBOX *, enum commands);
void ClearCheckBox(DBOX *, enum commands);
int CheckBoxSetting(DBOX *, enum commands);
WINDOW ControlWindow(DBOX *, enum commands);
/* ------------- help box prototypes ------------- */
void HelpFunction(void);
void LoadHelpFile(void);
#define swap(a,b){int x=a;a=b;b=x;}


<a name="01e0_0010">
<a name="01e0_0011">
<a name="01e0_0011">

/* ---------------- config.h -------------- */

#ifndef CONFIG_H
#define CONFIG_H



struct colors {
    /* ------------ colors ------------ */
    char ApplicationFG,  ApplicationBG;
    char NormalFG,       NormalBG;
    char ButtonFG,       ButtonBG;
    char ButtonSelFG,    ButtonSelBG;
    char DialogFG,       DialogBG;
    char ErrorBoxFG,     ErrorBoxBG;
    char MessageBoxFG,   MessageBoxBG;
    char HelpBoxFG,      HelpBoxBG;
    char InFocusTitleFG, InFocusTitleBG;

    char TitleFG,        TitleBG;
    char DummyFG,        DummyBG;
    char TextBoxFG,      TextBoxBG;
    char TextBoxSelFG,   TextBoxSelBG;
    char TextBoxFrameFG, TextBoxFrameBG;
    char ListBoxFG,      ListBoxBG;
    char ListBoxSelFG,   ListBoxSelBG;
    char ListBoxFrameFG, ListBoxFrameBG;
    char EditBoxFG,      EditBoxBG;
    char EditBoxSelFG,   EditBoxSelBG;
    char EditBoxFrameFG, EditBoxFrameBG;
    char MenuBarFG,      MenuBarBG;
    char MenuBarSelFG,   MenuBarSelBG;
    char PopDownFG,      PopDownBG;
    char PopDownSelFG,   PopDownSelBG;
    char InactiveSelFG;
    char ShortCutFG;
/* ----------- configuration parameters ----------- */
typedef struct config {
    char version[sizeof DFLAT_APPLICATION + sizeof VERSION];
    char mono;         /* 0=color, 1=mono, 2=reverse mono    */
    int InsertMode;    /* Editor insert mode                 */
    int Tabs;          /* Editor tab stops                   */
    int WordWrap;      /* True to word wrap editor           */
    int Border;        /* True for application window border */
    int Title;         /* True for application window title  */
    int Texture;       /* True for textured appl window      */
    int ScreenLines;   /* Number of screen lines (25/43/50)  */
    struct colors clr; /* Colors                             */
extern CONFIG cfg;
extern struct colors color, bw, reverse;
int LoadConfig(void);
void SaveConfig(void);


<a name="01e0_0012">
<a name="01e0_0013">
<a name="01e0_0013">

/* ---------- window.c ------------- */
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include "dflat.h"


int foreground, background;   /* current video colors */
static void TopLine(WINDOW, int, RECT);

/* --------- create a window ------------ */
WINDOW CreateWindow(
    CLASS class,              /* class of this window       */
    char *ttl,                /* title or NULL              */
    int left, int top,        /* upper left coordinates     */
    int height, int width,    /* dimensions                 */
    void *extension,          /* pointer to additional data */
    WINDOW parent,            /* parent of this window      */
    int (*wndproc)(struct window *,enum messages,PARAM,PARAM),
    int attrib)               /* window attribute           */
    WINDOW wnd = malloc(sizeof(struct window));
    if (wnd != NULLWND)    {
        int base;
        /* ----- height, width = -1: fill the screen ------- */
        if (height == -1)
            height = SCREENHEIGHT;
        if (width == -1)
            width = SCREENWIDTH;
        /* ----- coordinates -1, -1 = center the window ---- */
        if (left == -1)
            wnd->rc.lf = (SCREENWIDTH-width)/2;
            wnd->rc.lf = left;
        if (top == -1)
            wnd-> = (SCREENHEIGHT-height)/2;
            wnd-> = top;
        wnd->attrib = attrib;
        if (ttl != NULL)
            AddAttribute(wnd, TITLEBAR);
        if (wndproc == NULL)
            wnd->wndproc = classdefs[FindClass(class)].wndproc;
            wnd->wndproc = wndproc;
        /* ---- derive attributes of base classes ---- */
        base = class;
        while (base != -1)    {
            int tclass = FindClass(base);
            AddAttribute(wnd, classdefs[tclass].attrib);
            base = classdefs[tclass].base;
        if (parent && !TestAttribute(wnd, NOCLIP))    {
            /* -- keep upper left within borders of parent - */
            wnd->rc.lf = max(wnd->rc.lf,GetClientLeft(parent));
            wnd-> = max(wnd->,GetClientTop(parent));
        wnd->class = class;
        wnd->extension = extension;
        wnd->rc.rt = GetLeft(wnd)+width-1;
        wnd-> = GetTop(wnd)+height-1;
        wnd->ht = height;
        wnd->wd = width;
        wnd->title = NULL;
        if (ttl != NULL)
            InsertTitle(wnd, ttl);
        wnd->next = wnd->prev = wnd->dFocus = NULLWND;
        wnd->parent = parent;
        wnd->videosave = NULL;
        wnd->condition = ISRESTORED;
        wnd->restored_attrib = 0;
        wnd->RestoredRC = wnd->rc;
        wnd->PrevKeyboard = wnd->PrevMouse = NULL;
        wnd->DeletedText = NULL;
        SendMessage(wnd, CREATE_WINDOW, 0, 0);
        if (isVisible(wnd))
            SendMessage(wnd, SHOW_WINDOW, 0, 0);
    return wnd;
/* -------- add a title to a window --------- */
void AddTitle(WINDOW wnd, char *ttl)
    InsertTitle(wnd, ttl);
    SendMessage(wnd, BORDER, 0, 0);
/* ----- insert a title into a window ---------- */
void InsertTitle(WINDOW wnd, char *ttl)
    if ((wnd->title=realloc(wnd->title,strlen(ttl)+1)) != NULL)
        strcpy(wnd->title, ttl);
/* ------- write a character to a window area at x,y ------- */
void PutWindowChar(WINDOW wnd, int x, int y, int c)
    int x1 = GetLeft(wnd)+x;
    int y1 = GetTop(wnd)+y;
    if (isVisible(wnd))    {
        if (!TestAttribute(wnd, NOCLIP))    {
            WINDOW wnd1 = GetParent(wnd);
            while (wnd1 != NULLWND)    {
                /* --- clip character to parent's borders -- */
                if (x1 < GetClientLeft(wnd1)   ||
                    x1 > GetClientRight(wnd1)  ||
                    y1 > GetClientBottom(wnd1) ||
                    y1 < GetClientTop(wnd1))
                wnd1 = GetParent(wnd1);
        if (x1 < SCREENWIDTH && y1 < SCREENHEIGHT)
            wputch(wnd, c, x, y);
static char line[161];
/* ----- clip line if it extends below the bottom of parent window ------ */
static int clipbottom(WINDOW wnd, int y)
    if (!TestAttribute(wnd, NOCLIP))    {
        WINDOW wnd1 = GetParent(wnd);
        while (wnd1 != NULLWND)    {
            if (GetClientTop(wnd)+y > GetClientBottom(wnd1)+1)
                return TRUE;
            wnd1 = GetParent(wnd1);
    return GetTop(wnd)+y > SCREENHEIGHT;
/* -- clip portion of line that extends past right margin of parent window-- */
void clipline(WINDOW wnd, int x, char *ln)
    WINDOW pwnd = GetParent(wnd);
    int x1 = strlen(ln);
    int i = 0;
    if (!TestAttribute(wnd, NOCLIP))    {
        while (pwnd != NULLWND)    {
            x1 = GetClientRight(pwnd) - GetLeft(wnd) - x + 1;
            pwnd = GetParent(pwnd);
    else if (GetLeft(wnd) + x > SCREENWIDTH)
        x1 = SCREENWIDTH-GetLeft(wnd) - x;
    /* --- adjust the clipping offset for color controls --- */
    if (x1 < 0)
        x1 = 0;
    while (i < x1)    {
        if ((unsigned char) ln[i] == CHANGECOLOR)
            i += 3, x1 += 3;
        else if ((unsigned char) ln[i] == RESETCOLOR)
            i++, x1++;
    ln[x1] = '\0';
#define clipbottom(w,y) FALSE
/* ------ write a line to video window client area ------ */
void writeline(WINDOW wnd, char *str, int x, int y, int pad)
    static char wline[120];
    if (!clipbottom(wnd, y))
        char *cp;
        int len;
        int dif;
        memset(wline, 0, sizeof wline);
        len = LineLength(str);
        dif = strlen(str) - len;
        strncpy(wline, str, ClientWidth(wnd) + dif);
        if (pad)    {
            cp = wline+strlen(wline);
            while (len++ < ClientWidth(wnd)-x)
                *cp++ = ' ';
        clipline(wnd, x, wline);
        wputs(wnd, wline, x, y);
/* -- write a line to video window (including the border) -- */
void writefull(WINDOW wnd, char *str, int y)
    if (!clipbottom(wnd, y))    {
        strcpy(line, str);
        clipline(wnd, 0, line);
        wputs(wnd, line, 0, y);
RECT AdjustRectangle(WINDOW wnd, RECT rc)
    /* -------- adjust the rectangle ------- */
    if (TestAttribute(wnd, HASBORDER))    {
        if (RectLeft(rc) == 0)
        else if (RectLeft(rc) < RectRight(rc) &&
                RectLeft(rc) < WindowWidth(wnd)+1)
    if (TestAttribute(wnd, HASBORDER | TITLEBAR))    {
        if (RectTop(rc) == 0)
        else if (RectTop(rc) < RectBottom(rc) &&
                RectTop(rc) < WindowHeight(wnd)+1)
    RectRight(rc) = max(RectLeft(rc),min(RectRight(rc),WindowWidth(wnd)));
    RectBottom(rc) = max(RectTop(rc),min(RectBottom(rc),WindowHeight(wnd)));
    return rc;
/* -------- display a window's title --------- */
void DisplayTitle(WINDOW wnd, RECT *rcc)
    int tlen = min(strlen(wnd->title), WindowWidth(wnd)-2);
    int tend = WindowWidth(wnd)-3-BorderAdj(wnd);
    RECT rc;
    if (rcc == NULL)
        rc = RelativeWindowRect(wnd, WindowRect(wnd));
        rc = *rcc;
    rc = AdjustRectangle(wnd, rc);
    if (SendMessage(wnd, TITLE, LPARAM(rcc), 0))    {
        if (wnd == inFocus)    {
            foreground = cfg.clr.InFocusTitleFG;
            background = cfg.clr.InFocusTitleBG;
        else    {
            foreground = cfg.clr.TitleFG;
            background = cfg.clr.TitleBG;
        memset(line,' ',WindowWidth(wnd));
        if (wnd->condition != ISMINIMIZED)
            strncpy(line + ((WindowWidth(wnd)-2 - tlen) / 2),
                wnd->title, tlen);
        if (TestAttribute(wnd, CONTROLBOX))
            line[2-BorderAdj(wnd)] = CONTROLBOXCHAR;
        if (TestAttribute(wnd, MINMAXBOX))    {
            switch (wnd->condition)    {
                case ISRESTORED:
                    line[tend+1] = MAXPOINTER;
                    line[tend]   = MINPOINTER;
                case ISMINIMIZED:
                    line[tend+1] = MAXPOINTER;
                case ISMAXIMIZED:
                    line[tend]   = MINPOINTER;
                    line[tend+1] = RESTOREPOINTER;
        line[RectRight(rc)+1] = line[tend+3] = '\0';
        writeline(wnd, line+RectLeft(rc),
/* --- display right border shadow character of a window --- */
static void near shadow_char(WINDOW wnd, int y)
    int fg = foreground;
    int bg = background;
    int x = WindowWidth(wnd);
    int c = videochar(GetLeft(wnd)+x, GetTop(wnd)+y);

    if (TestAttribute(wnd, SHADOW) == 0)
    foreground = DARKGRAY;
    background = BLACK;
    PutWindowChar(wnd, x, y, c);
    foreground = fg;
    background = bg;
/* --- display the bottom border shadow line for a window -- */
static void near shadowline(WINDOW wnd, RECT rc)
    int i;
    int y = GetBottom(wnd)+1;

    if ((TestAttribute(wnd, SHADOW)) == 0)
    if (!clipbottom(wnd, WindowHeight(wnd)))    {
        int fg = foreground;
        int bg = background;
        for (i = 0; i < WindowWidth(wnd)+1; i++)
            line[i] = videochar(GetLeft(wnd)+i, y);
        line[i] = '\0';
        foreground = DARKGRAY;
        background = BLACK;
        clipline(wnd, 1, line);
        line[RectRight(rc)+1] = '\0';
        if (RectLeft(rc) == 0)
        wputs(wnd, line+RectLeft(rc), RectLeft(rc),WindowHeight(wnd));
        foreground = fg;
        background = bg;
/* ------- display a window's border ----- */
void RepaintBorder(WINDOW wnd, RECT *rcc)
    int y;
    int lin, side, ne, nw, se, sw;
    RECT rc, clrc;
    if (!TestAttribute(wnd, HASBORDER))
    if (rcc == NULL)    {
        rc = RelativeWindowRect(wnd, WindowRect(wnd));
        if (TestAttribute(wnd, SHADOW))    {
        rc = *rcc;
    clrc = AdjustRectangle(wnd, rc);
    if (wnd == inFocus)    {
        lin  = FOCUS_LINE;
        side = FOCUS_SIDE;
        ne   = FOCUS_NE;
        nw   = FOCUS_NW;
        se   = FOCUS_SE;
        sw   = FOCUS_SW;
    else    {
        lin  = LINE;
        side = SIDE;
        ne   = NE;
        nw   = NW;
        se   = SE;
        sw   = SW;
    line[WindowWidth(wnd)] = '\0';
    /* ---------- window title ------------ */
    if (TestAttribute(wnd, TITLEBAR))
        if (RectTop(rc) == 0)
            if (RectLeft(rc) < WindowWidth(wnd)-BorderAdj(wnd))
                DisplayTitle(wnd, &rc);
    foreground = FrameForeground(wnd);
    background = FrameBackground(wnd);
    /* -------- top frame corners --------- */
    if (RectTop(rc) == 0)    {
        if (RectLeft(rc) == 0)
            PutWindowChar(wnd, 0, 0, nw);
        if (RectLeft(rc) < WindowWidth(wnd))    {
            if (RectRight(rc) >= WindowWidth(wnd)-1)
                PutWindowChar(wnd, WindowWidth(wnd)-1, 0, ne);
            TopLine(wnd, lin, rc);
    /* ----------- window body ------------ */
    for (y = RectTop(rc); y <= RectBottom(rc); y++)    {
        int ch;
        if (y == 0 || y >= WindowHeight(wnd)-1)
        if (RectLeft(rc) == 0)
            PutWindowChar(wnd, 0, y, side);
        if (RectLeft(rc) < WindowWidth(wnd) &&
                RectRight(rc) >= WindowWidth(wnd)-1)    {
            if (TestAttribute(wnd, VSCROLLBAR))
                ch = (    y == 1 ? UPSCROLLBOX       :
                            y == WindowHeight(wnd)-2  ?
                                DOWNSCROLLBOX       :
                            y-1 == wnd->VScrollBox    ?
                                SCROLLBOXCHAR       :
                                SCROLLBARCHAR );
                ch = side;
            PutWindowChar(wnd, WindowWidth(wnd)-1, y, ch);
        if (RectRight(rc) == WindowWidth(wnd))
            shadow_char(wnd, y);
    if (RectTop(rc) <= WindowHeight(wnd)-1 &&
            RectBottom(rc) >= WindowHeight(wnd)-1)    {
        /* -------- bottom frame corners ---------- */
        if (RectLeft(rc) == 0)
            PutWindowChar(wnd, 0, WindowHeight(wnd)-1, sw);
        if (RectLeft(rc) < WindowWidth(wnd) &&
                RectRight(rc) >= WindowWidth(wnd)-1)
            PutWindowChar(wnd, WindowWidth(wnd)-1,
                WindowHeight(wnd)-1, se);
        /* ----------- bottom line ------------- */
        if (TestAttribute(wnd, HSCROLLBAR))    {
            line[0] = LEFTSCROLLBOX;
            line[WindowWidth(wnd)-3] = RIGHTSCROLLBOX;
            memset(line+1, SCROLLBARCHAR, WindowWidth(wnd)-4);
            line[wnd->HScrollBox] = SCROLLBOXCHAR;
        line[WindowWidth(wnd)-2] = line[RectRight(rc)] = '\0';
        if (RectLeft(rc) != RectRight(rc) ||
           (RectLeft(rc) && RectLeft(rc) < WindowWidth(wnd)-1))
        if (RectRight(rc) == WindowWidth(wnd))
            shadow_char(wnd, WindowHeight(wnd)-1);
    if (RectBottom(rc) == WindowHeight(wnd))
        /* ---------- bottom shadow ------------- */
        shadowline(wnd, rc);
static void TopLine(WINDOW wnd, int lin, RECT rc)
    if (TestAttribute(wnd, TITLEBAR | HASMENUBAR))
    if (RectLeft(rc) < RectRight(rc))    {
        /* ----------- top line ------------- */
        line[RectRight(rc)] = '\0';
        writeline(wnd, line+RectLeft(rc),
            RectLeft(rc)+1, 0, FALSE);
/* ------ clear the data space of a window -------- */
void ClearWindow(WINDOW wnd, RECT *rcc, int clrchar)
    if (isVisible(wnd))    {
        int y;
        RECT rc;
        if (rcc == NULL)
            rc = RelativeWindowRect(wnd, WindowRect(wnd));
            rc = *rcc;
        if (RectLeft(rc) == 0)
            RectLeft(rc) = BorderAdj(wnd);
        if (RectRight(rc) > WindowWidth(wnd)-1)
            RectRight(rc) = WindowWidth(wnd)-1;
        memset(line, clrchar, sizeof line);
        line[RectRight(rc)+1] = '\0';
        for (y = RectTop(rc); y <= RectBottom(rc); y++)    {
            if (y < TopBorderAdj(wnd) ||
                    y >= WindowHeight(wnd)-1)
/* -- adjust a window's rectangle to clip it to its parent -- */
static RECT near ClipRect(WINDOW wnd)
    RECT rc;
    rc = wnd->rc;
    if (TestAttribute(wnd, SHADOW))    {
    if (!TestAttribute(wnd, NOCLIP))    {
        WINDOW pwnd = GetParent(wnd);
        if (pwnd != NULLWND)    {
            RectTop(rc) = max(RectTop(rc),
            RectLeft(rc) = max(RectLeft(rc),
            RectRight(rc) = min(RectRight(rc),
            RectBottom(rc) = min(RectBottom(rc),
    RectRight(rc) = min(RectRight(rc), SCREENWIDTH-1);
    RectBottom(rc) = min(RectBottom(rc), SCREENHEIGHT-1);
    RectLeft(rc) = min(RectLeft(rc), SCREENWIDTH-1);
    RectTop(rc) = min(RectTop(rc), SCREENHEIGHT-1);
    return rc;
/* -- get the video memory that is to be used by a window -- */
void GetVideoBuffer(WINDOW wnd)
    RECT rc;
    int ht;
    int wd;
    rc = ClipRect(wnd);
    ht = RectBottom(rc) - RectTop(rc) + 1;
    wd = RectRight(rc) - RectLeft(rc) + 1;
    wnd->videosave = realloc(wnd->videosave, (ht * wd * 2));
    if (wnd->videosave != NULL)
        getvideo(rc, wnd->videosave);
/* --- restore the video memory that was used by a window -- */
void RestoreVideoBuffer(WINDOW wnd)
    if (wnd->videosave != NULL)    {
        RECT rc;
        rc = ClipRect(wnd);
        storevideo(rc, wnd->videosave);
        wnd->videosave = NULL;
/* ------ compute the logical line length of a window ------ */
int LineLength(char *ln)
    int len = strlen(ln);
    char *cp = ln;
    while ((cp = strchr(cp, CHANGECOLOR)) != NULL)    {
        len -= 3;
    cp = ln;
    while ((cp = strchr(cp, RESETCOLOR)) != NULL)    {
    return len;

Copyright © 1991, Dr. Dobb's Journal

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.