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

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


Channels ▼
RSS

C/C++

C Programming


JUN91: C PROGRAMMING

This article contains the following executables: DFLAT3.ARC ARCE.COM * Note: Use ARCE.COM to extract .ARC files including DFLAT3.ARC on this disk

This is the second installment of D-Flat, the new "C Programming" column project that I started last month. D-Flat is a C function library that implements the SAA Common User Access interface design in an event-driven programming model for MS-DOS text-mode applications. Last month we built the hardware-dependent code that deals with the mouse, the keyboard, and the screen. We also coded the compiler-specific stuff, the compile-time conditional code that distinguishes Turbo C from Microsoft C. The code from last month encapsulates most of the hardware and the compiler dependencies. If you wanted to port D-Flat to a different compiler or computer, you would modify that code. The code that appears from now on will be mostly independent of the hardware and the compiler, although there is an occasional #ifdef MSC to get around some compiler differences. This month we address the parts of the library that manage the configuration of a D-Flat application, that manage window classes, and that contain the low-level window drivers.

My approach to explaining D-Flat will use a tutorial format at first. As you progress through the series in the months to come, I will explain the different parts of the system as soon as the code being discussed uses them. This is a bottom-up approach. I want to get the low-level stuff out of the way so that we can concentrate on the implementation of the window classes and how your applications programs will use them. There are a lot of functions and macros in the API that will pass by without much in the way of explanation at first. The dflat.h header file from last month has many such macros. As we use them, I will explain them. The window.c file this month has code that supports clipping, but we don't need to get into clipping until we get past the basics of displaying and using windows.

When the series is completed, I will publish a complete programmer's reference guide to the messages, functions, and macros that make up the D-Flat API. In addition, I will publish a generic user's guide for D-Flat applications. You will be able to use that guide for any documentation that accompanies your application. Eventually these guides will get into the file of source code that you can download from CompuServe or Tele-Path, as explained later in this column.

Program Configuration

A D-Flat application program will be subject to some user-controlled configuration items. The user might be able to specify colors, editor options, and so on. The D-Flat library includes functions and data structures to support maintenance of the configuration. Listing One, page 148, is config.h, the header file that describes the configuration data structures. The first entry is a #define for the DFLAT_APPLICATION global symbol. This string names your application program and will be the name of the configuration file as well. As shown in Listing One, the string is initialized to the "memopad" string literal. The configuration file will, therefore, be MEMOPAD.CFG.

The colors structure in config.h describes the color configuration for each of the D-Flat window classes. The values are in pairs consisting of foreground and background colors for each class. Sometimes you will see two pairs for a class, such as ButtonFG, ButtonBG, ButtonSelFG, and ButtonSelBG. The second pair, which always has Sel in its identifier, specifies the colors for highlighted data items in the window class. These items include menu bars, listbox selectors, and marked blocks of text. Some window classes have definitions for the window's frame colors. These identifiers include the value Frame.

Some of the colors are for components of windows rather than for windows themselves. The Title and InFocusTitle values are the colors for the titles of all windows. The MenuBar values are for an application's menu bar. The InactiveSelFG and ShortCutFG colors are for the shortcut letter values in menu selections.

The CONFIG typedef in config.h defines the format for a configuration record. If your D-Flat application needs more configuration items -- and most of them will -- you add your custom configuration data objects to the CONFIG data type. Initially, there are five fields. The mono field, when true, specifies that the windows are to display with the monochrome colors regardless of the user's video system. The Insert-Mode field specifies whether the text editor for edit box windows starts out in insert or overwrite mode by default. The Tabs field specifies the tab width for the text editor. The WordWrap field specifies whether the editor wraps words at the right margin of multiple-line edit box windows or scrolls horizontally until the user presses the Enter key. The clr field contains the colors for the application.

Listing Two, page 148 is config.c, which contains the default initialized values for the configuration record and functions to read and write a configuration file. There are two arrays of colors. One is for color systems and one is for monochrome systems. To change the defaults, you would change these arrays. The arrays initialize themselves by using the color values from the Turbo C conio.h header file. The dflat.h header file, published last month, contains the same values for Microsoft C users.

The cfg structure is the instance of the CONFIG data type that contains the program's configuration values. If the application does not use custom configuration values, the defaults apply.

The LoadConfig and SaveConfig functions load and save the current configuration values from and to a file that is named according to the DFLAT_APPLICATION global variable.

Window Classes

D-Flat works with windows in a hierarchy of window classes. This model is similar to the one used by the Microsoft Windows programming platform. When you create a window, you specify its class, and D-Flat therefore knows how to deal with the window. The class identifies the window's colors and certain processing characteristics. An edit box window behaves differently than a list box window, for example. Listing Three, page 148, is classdef.h, the header file that defines the CLASSDEFS data type, which contains the description of a window class. The classdefs array of CLASSDEFS data types contains an entry for each class. Each entry specifies the window class that it describes, the base class -- if any -- from which it is derived, the window class's colors, the address of a window processing function to which all messages for the window are sent, and a window attribute value.

The window class values are defined in the dflat.h header file, which I published last month. If you want to add window classes to the hierarchy, you add an entry to the enum window_class in dflat.h and an entry to the classdefs array in Listing Four, page 148, classdefs.c. This technique is different from the way that Windows programmers do it. They put runtime code into their programs to register new classes. A D-Flat programmer uses the C compiler to register classes by coding the new classes into the class tables.

Each entry in the classdefs array defines the colors associated with the class. There are three sets of colors -- one for the window itself, one for highlighted selections in the window, and one for the window's frame. Observe that the color entries are pointers to color values. This allows a program to change the color configuration without changing this table. If a color entry is a NULL pointer, which is valid for highlighted colors, the value assumes the color of the window itself.

Each class has an associated window processing function, and the address of that function appears in the classdefs array entry for the class. When something sends a message to a window, the window processing function assigned to the window's class executes first. That function will then either process the message and return or call the function of the class from which the original class is derived.

The original D-Flat classes as published in classdef.h are: NORMAL, APPLICATION, TEXTBOX, LISTBOX, EDITBOX, MENUBAR, POPDOWNMENU, BUTTON, DIALOG, ERRORBOX, MESSAGEBOX, HELPBOX, and DUMMY.

Most of the window classes are derived from the NORMAL window class--either directly or by deriving from classes that derive from the NORMAL class. The NORMAL class manages those processes--such as creating, closing, moving, resizing, focusing, and so on--that can be common to all windows.

An APPLICATION window is the first window that a D-Flat application opens. It is the parent of the MENUBAR window and all the document windows.

A TEXTBOX window is one into which you can write text and with which the user can scroll and page through the text.

The LISTBOX and EDITBOX window classes are derived from the TEXTBOX class. A LISTBOX window consists of lines in a list with a selection cursor that the user can move up and down and with which the user can select from the entries in the list. An EDITBOX window contains text that the user enters by using text editor functions.

A MENUBAR window consists of a single line that appears on the first line of an APPLICATION window and contains menu selections. When the user makes a menu selection, the MENUBAR window opens a POPDOWNMENU window.

The POPDOWNMENU class is derived from the LISTBOX class. It is a single-page listbox that will send command messages to its parent as the result of user selections.

The BUTTON class defines a small static TEXTBOX window that reacts to user selections by sending its parent a message.

The DIALOG window class defines a window that hosts a set of other window types to implement a dialog box.

The ERRORBOX, MESSAGEBOX, and HELPBOX windows are DIALOG windows with defined messages and user actions assigned to them.

The DUMMY window class defines a ghost window frame that appears when the user is moving or sizing a window. You will learn all about these window classes in the months to come when we discuss them and implement their window processing modules.

Window Attributes

The classdefs array defines default window attributes for the window classes. When you create a window, it automatically has the attributes of the class to which it belongs, as well as all the classes from which it is derived. In addition, you can specify additional attributes when you create the window, as well as add and remove attributes from an existing window at any time. The following attribute codes are defined in classdef.h: SHADOW, MOVEABLE, SIZEABLE, HASMENUBAR,VSCROLLBAR, HSCROLLBAR, VISIBLE, SAVESELF, TITLEBAR, CONTROLBOX, MINMAXBOX, NOCLIP, READONLY, MULTILINE, and HASBORDER.

Most of the attributes indicate that a window includes a particular property. For example, the SHADOW attribute says that a window has a video shadow, the MOVEABLE attribute says that the user may move the window around the screen. Other attributes are not so obvious. The VISIBLE attribute says that the window is to be displayed when the program creates it. The NOCLIP attribute tells a window that it may display itself in regions of the screen that are outside of its parent. The READONLY attribute is for EDITBOX windows, the user may not change the text in a READONLY EDITBOX window. An EDITBOX window that does not have the MULTILINE attribute occupies one line only and does not wrap words or react to the Enter key.

The SAVESELF attribute says that a window is to save and restore video memory when it is created or displayed and closed or hidden. Windows that do not have this attribute do not save video memory when they are displayed. Furthermore, when they are hidden or closed, they send messages to all overlapping windows to repaint the parts of themselves that overlap the closing window. The SAVESELF attribute is an efficiency tactic for windows that keep the focus for as long as they are open. The POPDOWNMENU and DIALOG window classes have the SAVESELF attribute.

The Window Driver

Listing Five, page 150, is window.c, the driver module for D-Flat windows. It begins with the CreateWindow function. A D-Flat application calls this function to create a window. You must pass this function the window class, the text of the window's title, and the upper left screen coordinates relative to zero where the window will first display, its height and width, a pointer -- usually NULL -- to data space that stores additional data about the window, the WINDOW handle of the parent window, a pointer to a window processing module, and an attribute value.

The window's title may be NULL, in which case the window will have no title. If either of the window's upper left coordinates are - 1, the window will be centered on the associated axis. If you provide an address to a window processing function, messages to that window will be sent to the specified function first. A discussion of the window processing function's operation appears later in this series when we get into example programs. The attribute value that you pass to CreateWindow as its last parameter contains additional attributes that the window will have over and above those assigned to its class.

The CreateWindow function allocates memory for the window structure and sets the fields in the structure to their initial values as determined by the function arguments or by default values. The structure's format appears in dflat.h from last month. After the structure is initialized, the function sends a CREATE_WINDOW message to the window. We will discuss the mechanism for sending and processing window messages next month. If the window has the VISIBLE attribute, the function sends the SHOW_WINDOW message to the window. Finally, it returns the WINDOW handle to the function that called CreateWindow.

The window.c source file contains several window-driver functions that outside functions can call. Some of these support the D-Flat API and others are for use by D-Flat functions themselves. The AddTitle function accepts a WINDOW handle and a string title and adds the title to the window. The PutWindowChar function writes a single character to a window at a specified x,y coordinate. The clipbottom and clipline functions perform clipping of a window's display to keep it inside the screen and parent window borders. The writeline function writes a string to a window at a specified x, y coordinate, and pads to the window's border with spaces if told to do so. The RepaintBorder function displays the window's border including any scroll bars, the shadow, and the title bar. The ClearWindow function clears the data space of a window to spaces. The GetVideoBuffer and RestoreVideoBuffer functions manage the video memory save operations for windows that have the SAVESELF attribute. The LineLength function computes the logical length of a line of display data, adjusting for any embedded color controls in the text.

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 D-FLAT.ARC. It is a preliminary version but one that works. I will replace this file over the months as the code changes. At present, everything compiles and works with Turbo C 2.0 and Microsoft C 6.0. There is a makefile that the make utilities of both compilers accept, and there is one example program, the MEMOPAD program, with which I write some of these columns. If you want to discuss D-Flat with me, my CompuServe ID is 71101,1262, and I monitor the DDJ FORUM daily.

Next month we will get into D-Flat's event-driven programming model's message system.

The Programmer's Soap Box

Recent DDJ columns and articles have discussed the issue of software patents. Opinions vary and are strong as to whether software algorithms should be protected by patents. Given that some court decisions have ruled that they are, I wonder now how effective such protection really is. It brings to mind the PROMIS software system of the early '80s and the legal insanity that surrounds it. A recent column by the syndicated columnist James Kilpatrick recalled the case to my attention, and the account that follows comes mostly from his column. The PROMIS case does not address specific software patent issues, but it does raise questions about the ability of a developer to use whatever protection the law provides to safeguard his or her ideas and rights. Here's what happened.

A programmer named William Hamilton developed a system named PROMIS that manages law enforcement caseload databases. The Justice Department awarded him a contract so that federal lawyers could use PROMIS in the pursuit of their noble duties. Then, guess what? Our government simply defaulted on payment. They used the software, and they didn't pay for it. This wasn't shareware or something that they downloaded from a BBS. This was a contract, and Uncle Sam refused to honor its terms. There was no problem with the code. It worked fine. The guardians of our trust, the protectors of the Constitution, the nation's combined law firm and police department turned their back on an iron-clad obligation to a citizen. They kept their PROMIS but they didn't keep their promise, and Hamilton's company went belly-up.

Why did this happen? The government bureaucrat who was in charge of authorizing payment was a former employee of Hamilton, and Hamilton had fired him. Did the government guy's grudge against his former boss influence his decision to withhold payment? Only he knows.

A federal bankruptcy judge ruled that the Justice Department was in default and had in fact stolen the software. He ordered them to pay up. On appeal, the government lost again. They appealed again, and Hamilton still hasn't seen any money. Two separate Congressional investigations failed to shake the money tree. Every federal bankruptcy judge who has since been assigned to the case disqualifies himself for one vague reason or another. No one wants it because the first judge who ruled against the government--his own employer--was not reappointed when his term was up. To add insult to injury, the feds sold copies of PROMIS to the Canadian government and have freely distributed it among the U.S. intelligence community.

Given all this, how secure will you feel when your snappy new algorithm is protected by a bona fide, U.S. grade-A patent? Who will go to bat for you when someone uses your work? That, of course, will depend on how much money you have for lawyers and who the pirate is. Such protection exists only when it is in the interest of the guys with the big guns.


_C PROGRAMMING COLUMN_
by Al Stevens


[LISTING ONE]
<a name="016a_000b">

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

#ifndef CONFIG_H
#define CONFIG_H

#define DFLAT_APPLICATION "memopad"

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 mono;         /* True for B/W screens on any monitor */
    int InsertMode;    /* Editor insert mode                  */
    int Tabs;          /* Editor tab stops                    */
    int WordWrap;      /* True to word wrap editor            */
    struct colors clr; /* Colors                              */
} CONFIG;

extern CONFIG cfg;
extern struct colors color, bw;

void LoadConfig(void);
void SaveConfig(void);

#endif


<a name="016a_000c">
<a name="016a_000d">
[LISTING TWO]
<a name="016a_000d">

/* ------------- config.c ------------- */

#include <conio.h>
#include "dflat.h"

/* ----- default colors for color video system ----- */
struct colors color = {
    LIGHTGRAY, BLUE,  /* Application   */
    LIGHTGRAY, BLACK, /* Normal        */
    BLACK, CYAN,      /* Button        */
    WHITE, CYAN,      /* ButtonSel     */
    LIGHTGRAY, BLUE,  /* Dialog        */
    YELLOW, RED,      /* ErrorBox      */
    BLACK, LIGHTGRAY, /* MessageBox    */
    BLACK, LIGHTGRAY, /* HelpBox       */
    WHITE, CYAN,      /* InFocusTitle  */
    BLACK, CYAN,      /* Title         */
    GREEN, LIGHTGRAY, /* Dummy         */
    BLACK, LIGHTGRAY, /* TextBox       */
    LIGHTGRAY, BLACK, /* TextBoxSel    */
    LIGHTGRAY, BLUE,  /* TextBoxFrame  */
    BLACK, LIGHTGRAY, /* ListBox       */
    LIGHTGRAY, BLACK, /* ListBoxSel    */
    LIGHTGRAY, BLUE,  /* ListBoxFrame  */
    BLACK, LIGHTGRAY, /* EditBox       */
    LIGHTGRAY, BLACK, /* EditBoxSel    */
    LIGHTGRAY, BLUE,  /* EditBoxFrame  */
    BLACK, LIGHTGRAY, /* MenuBar       */
    BLACK, CYAN,      /* MenuBarSel    */
    BLACK, CYAN,      /* PopDown       */
    BLACK, LIGHTGRAY, /* PopDownSel    */
    DARKGRAY,         /* InactiveSelFG */
    RED               /* ShortCutFG    */
};

/* ----- default colors for mono video system ----- */
struct colors bw = {
    LIGHTGRAY, BLACK, /* Application   */
    LIGHTGRAY, BLACK, /* Normal        */
    BLACK, LIGHTGRAY, /* Button        */
    WHITE, LIGHTGRAY, /* ButtonSel     */
    LIGHTGRAY, BLACK, /* Dialog        */
    LIGHTGRAY, BLACK, /* ErrorBox      */
    LIGHTGRAY, BLACK, /* MessageBox    */
    BLACK, LIGHTGRAY, /* HelpBox       */
    BLACK, LIGHTGRAY, /* InFocusTitle  */
    BLACK, LIGHTGRAY, /* Title         */
    BLACK, LIGHTGRAY, /* Dummy         */
    LIGHTGRAY, BLACK, /* TextBox       */
    BLACK, LIGHTGRAY, /* TextBoxSel    */
    LIGHTGRAY, BLACK, /* TextBoxFrame  */
    LIGHTGRAY, BLACK, /* ListBox       */
    BLACK, LIGHTGRAY, /* ListBoxSel    */
    LIGHTGRAY, BLACK, /* ListBoxFrame  */
    LIGHTGRAY, BLACK, /* EditBox       */
    BLACK, LIGHTGRAY, /* EditBoxSel    */
    LIGHTGRAY, BLACK, /* EditBoxFrame  */
    LIGHTGRAY, BLACK, /* MenuBar       */
    BLACK, LIGHTGRAY, /* MenuBarSel    */
    BLACK, LIGHTGRAY, /* PopDown       */
    LIGHTGRAY, BLACK, /* PopDownSel    */
    DARKGRAY,         /* InactiveSelFG */
    WHITE             /* ShortCutFG    */
};

/* ------ default configuration values ------- */
CONFIG cfg = {
    FALSE,           /* mono                  */
    TRUE,            /* Editor Insert Mode    */
    4,               /* Editor tab stops      */
    TRUE             /* Editor word wrap      */
};

/* ------ load a configuration file from disk ------- */
void LoadConfig(void)
{
    FILE *fp = fopen(DFLAT_APPLICATION ".cfg", "rb");
    if (fp != NULL)    {
        fread(&cfg, sizeof(CONFIG), 1, fp);
        fclose(fp);
    }
}

/* ------ save a configuration file to disk ------- */
void SaveConfig(void)
{
    FILE *fp = fopen(DFLAT_APPLICATION ".cfg", "wb");
    if (fp != NULL)    {
        cfg.InsertMode = GetCommandToggle(ID_INSERT);
        cfg.WordWrap = GetCommandToggle(ID_WRAP);
        fwrite(&cfg, sizeof(CONFIG), 1, fp);
        fclose(fp);
    }
}



<a name="016a_000e">
<a name="016a_000f">
[LISTING THREE]
<a name="016a_000f">

/* ---------------- classdef.h --------------- */

#ifndef CLASSDEF_H
#define CLASSDEF_H

typedef struct classdefs {
    CLASS class;                        /* window class      */
    CLASS base;                         /* base window class */
    char *fg,*bg,*sfg,*sbg,*ffg,*fbg;   /* colors            */
    int (*wndproc)(struct window *,enum messages,PARAM,PARAM);
    int attrib;
} CLASSDEFS;

extern CLASSDEFS classdefs[];

#define SHADOW      0x0001
#define MOVEABLE    0x0002
#define SIZEABLE    0x0004
#define HASMENUBAR  0x0008
#define VSCROLLBAR  0x0010
#define HSCROLLBAR  0x0020
#define VISIBLE     0x0040
#define SAVESELF    0x0080
#define TITLEBAR    0x0100
#define CONTROLBOX  0x0200
#define MINMAXBOX   0x0400
#define NOCLIP      0x0800
#define READONLY    0x1000
#define MULTILINE   0x2000
#define HASBORDER   0x4000

int FindClass(CLASS);
#define DerivedClass(class) (classdefs[FindClass(class)].base)

#endif




<a name="016a_0010">
<a name="016a_0011">
[LISTING FOUR]
<a name="016a_0011">

/* ---------------- classdef.c ---------------- */

#include <stdio.h>
#include "dflat.h"

/* Add class definitions to this table.
 * Add the class symbol to the CLASS list in dflat.h
 */

CLASSDEFS classdefs[] = {
    {   /* ---------- NORMAL Window Class ----------- */
        NORMAL,
        -1,
        &cfg.clr.NormalFG, &cfg.clr.NormalBG,
        NULL, NULL,
        &cfg.clr.NormalFG, &cfg.clr.NormalBG,
        NormalProc
    },
    {   /* ---------- APPLICATION Window Class ----------- */
        APPLICATION,
        NORMAL,
        &cfg.clr.ApplicationFG, &cfg.clr.ApplicationBG,
        NULL, NULL,
        &cfg.clr.ApplicationFG, &cfg.clr.ApplicationBG,
        ApplicationProc,
        VISIBLE | SAVESELF | CONTROLBOX | TITLEBAR | HASBORDER
    },
    {   /* ------------ TEXTBOX Window Class -------------- */
        TEXTBOX,
        NORMAL,
        &cfg.clr.TextBoxFG, &cfg.clr.TextBoxBG,
        &cfg.clr.TextBoxSelFG, &cfg.clr.TextBoxSelBG,
        &cfg.clr.TextBoxFrameFG, &cfg.clr.TextBoxFrameBG,
        TextBoxProc
    },
    {   /* ------------- LISTBOX Window class ------------- */
        LISTBOX,
        TEXTBOX,
        &cfg.clr.ListBoxFG, &cfg.clr.ListBoxBG,
        &cfg.clr.ListBoxSelFG, &cfg.clr.ListBoxSelBG,
        &cfg.clr.ListBoxFrameFG, &cfg.clr.ListBoxFrameBG,
        ListBoxProc
    },
    {   /* ------------- EDITBOX Window Class -------------- */
        EDITBOX,
        TEXTBOX,
        &cfg.clr.EditBoxFG, &cfg.clr.EditBoxBG,
        &cfg.clr.EditBoxSelFG, &cfg.clr.EditBoxSelBG,
        &cfg.clr.EditBoxFrameFG, &cfg.clr.EditBoxFrameBG,
        EditBoxProc
    },
    {   /* ------------- MENUBAR Window Class --------------- */
        MENUBAR,
        NORMAL,
        &cfg.clr.MenuBarFG, &cfg.clr.MenuBarBG,
        &cfg.clr.MenuBarSelFG, &cfg.clr.MenuBarSelBG,
        NULL, NULL,
        MenuBarProc,
        VISIBLE
    },
    {   /* ------------- POPDOWNMENU Window Class ----------- */
        POPDOWNMENU,
        LISTBOX,
        &cfg.clr.PopDownFG, &cfg.clr.PopDownBG,
        &cfg.clr.PopDownSelFG, &cfg.clr.PopDownSelBG,
        NULL, NULL,
        PopDownProc,
        SAVESELF | NOCLIP | HASBORDER
    },
    {   /* ----------- BUTTON Window Class --------------- */
        BUTTON,
        TEXTBOX,
        &cfg.clr.ButtonFG, &cfg.clr.ButtonBG,
        &cfg.clr.ButtonSelFG, &cfg.clr.ButtonSelBG,
        NULL, NULL,
        ButtonProc,
        SHADOW
    },
    {   /* ------------- DIALOG Window Class -------------- */
        DIALOG,

        NORMAL,
        &cfg.clr.DialogFG, &cfg.clr.DialogBG,
        NULL, NULL,
        &cfg.clr.DialogFG, &cfg.clr.DialogBG,
        DialogProc,
        SHADOW | MOVEABLE | SAVESELF | CONTROLBOX | HASBORDER
    },
    {   /* ------------ ERRORBOX Window Class ----------- */
        ERRORBOX,
        DIALOG,
        &cfg.clr.ErrorBoxFG, &cfg.clr.ErrorBoxBG,
        NULL, NULL,
        &cfg.clr.ErrorBoxFG, &cfg.clr.ErrorBoxBG,
        DialogProc,
        SHADOW | HASBORDER
    },
    {   /* --------- MESSAGEBOX Window Class ------------- */
        MESSAGEBOX,
        DIALOG,
        &cfg.clr.MessageBoxFG, &cfg.clr.MessageBoxBG,
        NULL, NULL,
        &cfg.clr.MessageBoxFG, &cfg.clr.MessageBoxBG,
        DialogProc,
        SHADOW | HASBORDER
    },
    {   /* ----------- HELPBOX Window Class --------------- */
        HELPBOX,
        DIALOG,
        &cfg.clr.HelpBoxFG, &cfg.clr.HelpBoxBG,
        NULL, NULL,
        &cfg.clr.HelpBoxFG, &cfg.clr.HelpBoxBG,
        DialogProc,
        SHADOW | HASBORDER
    },
    {   /* -------------- DUMMY Window Class ---------------- */
        DUMMY,
        -1,
        &cfg.clr.DummyFG, &cfg.clr.DummyBG,
        NULL, NULL,
        &cfg.clr.DummyFG, &cfg.clr.DummyBG,
        NULL,
        HASBORDER
    }
};

/* ------- return the offset of a class into the class
                 definition table ------ */
int FindClass(CLASS class)
{
    int i;
    for (i = 0; i < sizeof(classdefs) / sizeof(CLASSDEFS); i++)
        if (class == classdefs[i].class)
            return i;
    return 0;
}






<a name="016a_0012">
<a name="016a_0013">
[LISTING FIVE]
<a name="016a_0013">

/* ---------- window.c ------------- */

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include "dflat.h"

WINDOW inFocus = NULLWND;

int foreground, background;   /* current video colors */

static void InsertTitle(WINDOW, char *);
static void DisplayTitle(WINDOW, 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));
    get_videomode();
    if (wnd != NULLWND)    {
        int base;
        /* ----- coordinates -1, -1 = center the window ---- */
        if (left == -1)
            wnd->rc.lf = (SCREENWIDTH-width)/2;
        else
            wnd->rc.lf = left;
        if (top == -1)
            wnd->rc.tp = (SCREENHEIGHT-height)/2;
        else
            wnd->rc.tp = top;
        wnd->attrib = attrib;
        if (ttl != NULL)
            AddAttribute(wnd, TITLEBAR);
        if (wndproc == NULL)
            wnd->wndproc = classdefs[FindClass(class)].wndproc;
        else
            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->rc.tp = max(wnd->rc.tp, GetClientTop(parent) +
                                (TestAttribute(parent, HASMENUBAR) ? 1 : 0));
        }
        wnd->class = class;
        wnd->extension = extension;
        wnd->rc.rt = GetLeft(wnd)+width-1;
        wnd->rc.bt = GetTop(wnd)+height-1;
        wnd->ht = height;
        wnd->wd = width;
        wnd->title = ttl;
        if (ttl != NULL)
            InsertTitle(wnd, ttl);
        wnd->next = wnd->prev = wnd->dFocus = NULLWND;
        wnd->parent = parent;
        wnd->videosave = NULL;
        wnd->condition = ISRESTORED;
        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 ---------- */
static void InsertTitle(WINDOW wnd, char *ttl)
{
    if ((wnd->title = malloc(strlen(ttl)+1)) != NULL)
        strcpy(wnd->title, ttl);
}

/* ------- write a character to a window at x,y ------- */
void PutWindowChar(WINDOW wnd, int x, int y, int c)
{
    int x1 = GetClientLeft(wnd)+x;
    int y1 = GetClientTop(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)    ||
                    (y1 < GetTop(wnd1)+2 &&
                            TestAttribute(wnd1, HASMENUBAR)))
                        return;
                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 the
             parent window ------ */
static int clipbottom(WINDOW wnd, int y)
{
    if (!TestAttribute(wnd, NOCLIP))    {
        WINDOW wnd1 = GetParent(wnd);
        while (wnd1 != NULLWND)    {
            if (GetClientTop(wnd)+y > GetBottom(wnd1))
                return TRUE;
            wnd1 = GetParent(wnd1);
        }
    }
    return GetClientTop(wnd)+y > SCREENHEIGHT;
}

/* ------ clip the portion of a line that extends past the
                     right margin of the 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 = GetRight(pwnd) - GetLeft(wnd) - x;
            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++;
        else
            i++;
    }
    ln[x1] = '\0';
}

/* ------ write a line to video window client area ------ */
void writeline(WINDOW wnd, char *str, int x, int y, int pad)
{
    char wline[120];

    if (TestAttribute(wnd, HASBORDER))    {
        x++;
        y++;
    }
    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);
    }
}

/* -------- display a window's title --------- */
static void DisplayTitle(WINDOW wnd, RECT rc)
{
    int tlen = min(strlen(wnd->title), WindowWidth(wnd)-2);
    int tend = WindowWidth(wnd)-4;

    if (SendMessage(wnd, TITLE, 0, 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)-2);
        if (wnd->condition != ISMINIMIZED)
            strncpy(line + ((WindowWidth(wnd)-2 - tlen) / 2),
                wnd->title, tlen);
        line[WindowWidth(wnd)-2] = '\0';
        if (TestAttribute(wnd, CONTROLBOX))
            line[1] = CONTROLBOXCHAR;
        if (TestAttribute(wnd, MINMAXBOX))    {
            switch (wnd->condition)    {
                case ISRESTORED:
                    line[tend+1] = MAXPOINTER;
                    line[tend]   = MINPOINTER;
                    break;
                case ISMINIMIZED:
                    line[tend+1] = MAXPOINTER;
                    break;
                case ISMAXIMIZED:
                    line[tend]   = MINPOINTER;
                    line[tend+1] = RESTOREPOINTER;
                    break;
                default:
                    break;
            }
        }
        line[RectRight(rc)+1] = '\0';
        writeline(wnd, line+RectLeft(rc),
                    RectLeft(rc), -1, FALSE);
    }
}

/* --- 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+1);

    if (TestAttribute(wnd, SHADOW) == 0)
        return;
    foreground = SHADOWFG;
    background = BLACK;
    PutWindowChar(wnd, x-1, 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)
        return;
    if (!clipbottom(wnd, WindowHeight(wnd)))    {
        int fg = foreground;
        int bg = background;
        for (i = 0; i < WindowWidth(wnd); i++)
            line[i] = videochar(GetLeft(wnd)+i+1, y);
        line[i] = '\0';
        foreground = SHADOWFG;
        background = BLACK;
        clipline(wnd, 1, line);
        line[RectRight(rc)+3] = '\0';
        wputs(wnd, line+RectLeft(rc), 1+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))
        return;
    if (rcc == NULL)    {
        rc = SetRect(0, 0, WindowWidth(wnd)-1,
                WindowHeight(wnd)-1);
        if (TestAttribute(wnd, SHADOW))    {
            rc.rt++;
            rc.bt++;
        }
    }
    else
        rc = *rcc;
    clrc = rc;
    /* -------- adjust the client rectangle ------- */
    if (RectLeft(rc) == 0)
        --clrc.rt;
    else
        --clrc.lf;
    if (RectTop(rc) == 0)
        --clrc.bt;
    else
        --clrc.tp;
    RectRight(clrc) = min(RectRight(clrc), WindowWidth(wnd)-3);
    RectBottom(clrc) =
                     min(RectBottom(clrc), WindowHeight(wnd)-3);
    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 (RectTop(rc) == 0)
        if (TestAttribute(wnd, TITLEBAR))
            DisplayTitle(wnd, clrc);
    foreground = FrameForeground(wnd);
    background = FrameBackground(wnd);
    /* -------- top frame corners --------- */
    if (RectTop(rc) == 0)    {
        if (RectLeft(rc) == 0)
            PutWindowChar(wnd, -1, -1, nw);
        if (RectRight(rc) >= WindowWidth(wnd)-1)
            PutWindowChar(wnd, WindowWidth(wnd)-2, -1, ne);

        if (TestAttribute(wnd, TITLEBAR) == 0)    {
            /* ----------- top line ------------- */
            memset(line,lin,WindowWidth(wnd)-1);
            line[RectRight(clrc)+1] = '\0';
            if (strlen(line+RectLeft(clrc)) > 1 ||
                            TestAttribute(wnd, SHADOW) == 0)
                writeline(wnd, line+RectLeft(clrc),
                        RectLeft(clrc), -1, FALSE);
        }
    }
    /* ----------- window body ------------ */
    for (y = 0; y < ClientHeight(wnd); y++)    {
        int ch;
        if (y >= RectTop(clrc) && y <= RectBottom(clrc))    {
            if (RectLeft(rc) == 0)
                PutWindowChar(wnd, -1, y, side);
            if (RectRight(rc) >= ClientWidth(wnd))    {
                if (TestAttribute(wnd, VSCROLLBAR))
                    ch = (    y == 0 ? UPSCROLLBOX      :
                              y == WindowHeight(wnd)-3  ?
                                   DOWNSCROLLBOX        :
                              y == wnd->VScrollBox      ?
                                   SCROLLBOXCHAR        :
                                   SCROLLBARCHAR );
                else
                    ch = side;
                PutWindowChar(wnd, WindowWidth(wnd)-2, y, ch);
            }
            if (RectRight(rc) == WindowWidth(wnd))
                shadow_char(wnd, y);
        }
    }
    if (RectBottom(rc) >= WindowHeight(wnd)-1)    {
        /* -------- bottom frame corners ---------- */
        if (RectLeft(rc) == 0)
            PutWindowChar(wnd, -1, WindowHeight(wnd)-2, sw);
        if (RectRight(rc) >= WindowWidth(wnd)-1)
            PutWindowChar(wnd, WindowWidth(wnd)-2,
                WindowHeight(wnd)-2, se);
        /* ----------- bottom line ------------- */
        memset(line,lin,WindowWidth(wnd)-1);
        if (TestAttribute(wnd, HSCROLLBAR))    {
            line[0] = LEFTSCROLLBOX;
            line[WindowWidth(wnd)-3] = RIGHTSCROLLBOX;
            memset(line+1, SCROLLBARCHAR, WindowWidth(wnd)-4);
            line[wnd->HScrollBox] = SCROLLBOXCHAR;
        }
        line[RectRight(clrc)+1] = '\0';
        if (strlen(line+RectLeft(clrc)) > 1 ||
                        TestAttribute(wnd, SHADOW) == 0)
            writeline(wnd,
                line+RectLeft(clrc),
                RectLeft(clrc),
                WindowHeight(wnd)-2,
                FALSE);
        if (RectRight(rc) == WindowWidth(wnd))
            shadow_char(wnd, WindowHeight(wnd)-2);
    }
    if (RectBottom(rc) == WindowHeight(wnd))
        /* ---------- bottom shadow ------------- */
        shadowline(wnd, clrc);
}

/* ------ 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 = SetRect(0, 0, ClientWidth(wnd)-1,
                ClientHeight(wnd)-1);
        else
            rc = *rcc;
        SetStandardColor(wnd);
        memset(line, clrchar, RectWidth(rc));
        line[RectWidth(rc)] = '\0';
        for (y = RectTop(rc); y <= RectBottom(rc); y++)
            writeline(wnd, line, RectLeft(rc), y, FALSE);
    }
}

/* -- adjust a window's rectangle to clip it to its parent -- */
static RECT near AdjustRect(WINDOW wnd)
{
    RECT rc = wnd->rc;
    if (TestAttribute(wnd, SHADOW))    {
        RectBottom(rc)++;
        RectRight(rc)++;
    }
    if (!TestAttribute(wnd, NOCLIP))    {
        WINDOW pwnd = GetParent(wnd);
        if (pwnd != NULLWND)    {
            RectTop(rc) = max(RectTop(rc),
                        GetClientTop(pwnd));
            RectLeft(rc) = max(RectLeft(rc),
                        GetClientLeft(pwnd));
            RectRight(rc) = min(RectRight(rc),
                        GetClientRight(pwnd));
            RectBottom(rc) = min(RectBottom(rc),
                        GetClientBottom(pwnd));
        }
    }
    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 = AdjustRect(wnd);
    ht = RectBottom(rc) - RectTop(rc) + 1;
    wd = RectRight(rc) - RectLeft(rc) + 1;
    wnd->videosave = realloc(wnd->videosave, (ht * wd * 2));
    get_videomode();
    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 = AdjustRect(wnd);
        storevideo(rc, wnd->videosave);
        free(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)    {
        cp++;
        len -= 3;
    }
    cp = ln;
    while ((cp = strchr(cp, RESETCOLOR)) != NULL)    {
        cp++;
        --len;
    }
    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.