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


JUL92: C PROGRAMMING

This article contains the following executables: DFLT12.ARC D12TXT.ARC

I have just returned from the Borland Developer's Conference in Monterey, California. This is the conference to attend if you use Borland products and like parties. The atmosphere is laid back, yet the technical information flows fast and free. The only stuffy part of the conference is the dreary Microsoft bashing that permeates every Borland presentation. Other than for that, we had a great time. I sat on an evening panel of distinguished authors who advised the attendees how to become writers and what to expect from writing in the field of computer literature. The session followed the Borland wine and cheese party and was marked by jet lag, beer on tap, and an occasional difference of opinion. A good time was had. A party at the Monterey aquarium featured a string quartet, a flute and bass jazz duo with Philippe Kahn on flute, buffet banquets of all kinds of food, and, of course, lots of live fish under glass to look at. Another evening ended with Philippe's Turbo Jazz band conducting a sit-in jam session. After listening to about an hour of bone-jarring fusion, I took a turn at the Steinway and asked about playing some real jazz. We did, and the room swung. Philippe has found his voice in the flute.

Grady Booch addressed the conference about the methods in his book, Object-Oriented Design with Applications (Benjamin Cummings, 1991). This book is the authoritative contemporary work on object-oriented analysis, design, and programming. It very nicely filled in most of the blanks in my experience and corrected a few misconceptions. After reading the first two chapters, I drew some conclusions. First, I must reluctantly admit that if I had not already learned what object-oriented programming is by using C++, I would not have understood the book. Not that the book is hard to read. Quite the opposite, in fact. However, a programmer needs some personal experience to relate to the concepts and examples in any explanation of OOP, and although Booch's is the best, it still remains that the paradigm shift cannot be taught. It can only be learned. My second conclusion is that you can probably teach object-oriented programming to non-programmers as if nothing else existed. If they have no paradigm already in place, no shift is required, and the shift is the real problem -- not the paradigm.

D-Flat++

I guess it's going to be called that. I'm well into the first layer of D-Flat++, and I am forming some positive opinions about the suitability of C++ as an alternative to the event-driven, message-based paradigm of Windows, D-Flat, and others. The first thing you will see when you compare similar processes in the two D-Flat libraries is that the C++ code is a lot less complex. For one thing, when you build member functions into classes, the compiler manages all the pointer dereferencing. You don't see it in your code, and the results are much cleaner.

You will see another obvious improvement in DF++'s string handling. Everybody builds a string class, and so did I. Where D-Flat has long sequences of specialized strcpys, strcats, *cp++, memsets, and so on, DF++ simply instantiates a string, concatenates it with another string, pulls a substring out of it, and so on. The code is easier to read, and there are a lot fewer lines of it. The notational improvements are dramatic.

C++ influences a program's design. You tend to use more classes because encapsulation protects data items from other parts of the program. You feel more secure about the integrity of data variables knowing that encapsulation will prevent you from making unbridled accesses to hidden members from distant parts of the program. A properly designed C++ program will rarely hear the telltale slap on the forehead of a programmer who just uncovered some long-forgotten use of an innocent variable.

If you haven't tried C++ yet, you might not see much point to it. If you have, I'm singing to the choir. That's the consensus. C programmers do not see the advantages of C++ until they have plunged, whereupon they become converts. This is no evangelization of OOP, however. I'll leave that to others. This is an endorsement of the C++ language's facility to build abstract data types, hide information, and improve program notation. Call it what you will, but I'm a third of the way through, and so far the notation is clearer, the code is smaller, and there are no external variables in D-Flat++ other than the ones that are static members of classes, and it would be unseemly to build any.

Grady Booch holds that if you are using C++ merely as an improved C, then you are missing and perhaps even abusing the power of the language. I am coming through experience to agree with that opinion.

Lovelier the Second Time Around

I have long believed that you should build a software system twice. The first one is for teaching you how the system ought to work. You throw it away and do the job right the second time. We seldom get that opportunity because the bean counters would never allow it. Version 2 always builds on version 1, which has already been paid for, correct or not. But D-Flat++ is not a C++ shroud wrapped around the D-Flat C library. It is a complete rewrite in C++ because one of the objectives is to use the advantages of the C++ language to build a user-interface API. So, knowing that I am going to rewrite all the code anyway, when I prepare to implement a particular feature, I stop to consider how that feature gave me trouble in D-Flat, and I design the trouble away. The result is a second chance to do the job right. There are no bean counters in charge of D-Flat, so I get to make those decisions. Programmers 1, bean counters 0.

D-Flat Message Boxes et al.

Back to the old days. Last month I described how D-Flat implements the dialog-box window class. A dialog box consists of the window itself and a number of control windows into which the user enters data or commands. In months past you learned about the edit-box and list-box window classes, both of which can be control windows on a dialog box. There are several more control window classes, which are derived from other classes and which implement the buttons and boxes that you can put on a dialog box. The dialog boxes in the memopad example application use all of these control windows. I will discuss each one by describing the source file that implements it. They all have the usual format for a class's source file. The window-processing module and supporting functions are in a stand-alone C-source file.

The Box

Listing One, page 142, is box.c, the source file that implements the box window class. Its purpose is to draw a rectangle with a label around other controls on the dialog box. The box is simple. It does its job unobtrusively by refusing to accept the focus and passing mouse messages to its parent window, the dialog box that hosts it. The border message displays the box's text label over the top edge of the border starting in the first position past the upper-left corner.

Listing Two, page 142, is button.c, the source file that implements pushbuttons. A pushbutton displays as a single line of text with a different color than its parent window and with a shadow made of half-height block characters from the graphics character set. When the user presses the button, either with the mouse or the keyboard, the program displays the button in a pushed configuration and then waits for the key or button release to paint the button in its original configuration. Then it sends the button's associated COMMAND message to the parent window.

Listing Three, page 142, is checkbox.c, the source file that implements the check-box control. A check box records a toggled option setting. It displays with the text characters [X] when the option is toggled on and with [ ] when it is off. The user toggles the setting by clicking the check box with the mouse or by selecting it with the keyboard and pressing the space bar. The keyboard cursor displays where the X goes when the check box is selected to provide the user a visual clue. The CheckBoxSetting function returns True if the specified check box is toggled on and False if it is not. The setting for a dialog box persists between uses of the dialog box, so the program can call the function at any time.

Listing Four, page 142, is combobox.c, the source file that implements the combo-box control. A combo box is a combination of a single-line edit box and a drop-down list box, thus its name. The combo-box class is derived from the edit-box class. When the program creates the control, the combo box's CREATE_WINDOW message creates the associated list box, but does not display it. The PAINT message adds a down-pointing arrow at the end of the single-line edit box. That token is the scroll button that the user clicks to drop down the list box. If the user clicks the token or presses the down-arrow key while the combo box has the focus, the program sends the SETFOCUS message to the list box, which then displays itself. The ListProc function is the window-processing module for the combo box's drop-down list box. When the user moves the list box's selection cursor, the list-box class sends itself the LB_SELECTION, which this module intercepts. It sends the text of the current selection to the edit-box component of the combo box. The application program calls PutComboListText to add lines of text to the drop-down list box after it opens the dialog box.

Listing Five, page 143, is msgbox.c, the code that implements a generic message dialog box and some specialized ones. Several macros in dflat.h call the GenericMessage function to display and process canned message boxes, among them the MessageBox, the YesNoBox, the ErrorBox, the CancelBox, and the InputBox. Each of these has its own window-processing module, to process messages in its own way. The MomentaryMessage function displays a message box that the caller must close. This process allows a program to post a "please stand by" message box while some lengthy process is under way. The program closes the window when the process is done. The slider box and the watch icon, described soon, are two other ways to tell the user that a time-consuming process is going on.

Listing Six, page 147, is radio.c, the code that implements the radio button. Besides the window-processing module which paints the radio button and processes its keyboard and mouse actions, the source file includes the PushRadioButton function which selects a specified radio button and the RadioButtonSetting function which tests the on/off state of a specified radio button. Both functions accept a pointer to the dialog box and the command code associated with the radio button.

Listing Seven, page 147, is slidebox.c, the code that implements the slider box. A slider box is a temporary display that a program uses to show the user the progress of a lengthy process. The program calls the SliderBox function with the length in characters of the slider box, a title for the slider box's dialog window, and a text message to display above the slider box itself. The function builds and displays the slider box dialog and returns its WINDOW handle to the caller. The caller then sends frequent PAINT messages with a percentage of completion expressed in the second parameter. The value will range from 0 to 100. Until the last value is sent, the PAINT message returns a true value. When the PAINT message arrives with a percent that is equal to or greater than 100, the window closes, and the PAINT message returns a false value. If the user selects the Cancel command button before the process is complete, the window closes and the PAINT message returns a false value. The memopad example program uses a slider box to display the progress of a printing file.

Listing Eight, page 148, is spinbutt.c, the code that implements the spin button. A spin button is a one-line list box with up and down arrows at the extreme right of the text. The user can scroll through the values with the up- and down-arrow keys or by clicking the up- and down-arrow characters. At any given time, the spin button control represents the currently displayed value. The print-setup dialog box in the memopad example program uses spin buttons to control the print margins.

Listing Nine, page 148, is text.c, the source code that implements the static text-display control on a dialog box. The text control is used to label other controls, and it can have a highlighted character to indicate the Alt key combination that selects the associated control.

Listing Ten, page 148, is watch.c, the source file that implements the wristwatch icon. A program can call the WatchIcon function to change the mouse cursor into a tiny window that resembles a watch dial as well as text mode can do it. The WatchIcon function returns the WINDOW handle of the icon. The program can send the icon window a CLOSE_WINDOW message to return to the normal mouse cursor. The watch icon is D-Flat's version of the Windows hourglass cursor.

How to Get D-Flat Now

The D-Flat source code is on CompuServe in Library 0 of the DDJ Forum and on M&T Online. If you cannot use either online service, send a formatted 360K or 720K diskette and an addressed, stamped diskette mailer to me in care of Dr. Dobb's Journal, 411 Borel Ave., San Mateo, CA 94402. I'll send you the latest version of D-Flat. The software is free, but if you'd care to, stuff a dollar bill in the mailer for the Brevard County Food Bank. They help the homeless and hungry. We call it DDJ's program of "careware." If you want to discuss D-Flat with me, use CompuServe. My ID is 71101, 1262, and I monitor the DDJ Forum daily.

Trouble Right Here...

Professor Harold Hill mounts the base of the town-square statue to alert the citizenry of an impending disaster. To the citizens the trouble is real or imagined, depending on how they feel about pool tables and their social consequences. Professor Hill's cure was a boy's band. We've got trouble right here, too. Please observe me if you will....

Time was when C compilers had no debuggers. They just compiled the best code they could, and we used printf and getchar to look at variables and set breakpoints -- to debug. It's hard to believe that we ever debugged that way, but we did. It was slow, but it worked. Along came source-level debuggers, and we were hooked. You'll never catch me going back to the old ways, says I. There was a cost, however. The compilers had to put some debugging data into the executable files so the debuggers could associate the executable code with the source code. That's OK, they said, the debugging information is passive except when you are debugging. It adds to the size of the executable file, but there is no performance penalty when you are not debugging, and compiling without debugging information merely strips the inert data from the executable file without changing the effects of the executing code. Sounds like a good idea to me.

Not long ago I got the early incarnations of D-Flat++ running with Borland C++ 3.0. The executable file was 175K, and I wanted to see how it looked without debugging information. I compiled without it and was pleased to see that the executable file was now only 48K. So far, so good. But when I tried to run the program, it blew up right away, leaving bug droppings all over the phosphor. I put the debugging information back in, and the program ran fine. What to do? How can I debug a bug that is only a bug when the debugger is not there? How, indeed? printf and getchar, oops, I mean cout and cin, that's how. Rats. I bit that bullet, and lo and behold discovered that the compiler's peekb function was reading bad values from BIOS RAM. My program thinks the screen dimensions are 0 by 118. That will never do.

To continue the quest, I recompiled to assembly language and learned that the compiler's inline peekb function was compiling into bum code. OK, so BC++ 3.0 has a bug of its own. Stuff happens. They'll fix it. But wait. Why does the program work when the debugging code is there? Isn't debugging information supposed to be nonintrusive? I compiled to assembly language with debugging information and found that the peekb function call is not expanded inline like a macro, but that it actually calls a real function named peekb. The compiled debuggable code is completely different from the debuggerless version. What kind of debuggery is this?

Pete Becker of Borland set the matter straight, and it's something you should know about. When you compile with debugging information included, BC++ does not expand inline functions into inline code unless you use the -vi option. Therefore, you should do most of your final testing either without debugging information or with the -vi option enabled. Otherwise you might be distributing a different program than you tested, and that would be trouble, I said trouble right here, and pretty soon we'd be needing a boy's band.

Oh think, my friends, how can any compiler ever hope to compete with a slide trombone...



_C PROGRAMMING COLUMN_
by Al Stevens


[LISTING ONE]
<a name="019c_000b">

/* ----------- box.c ------------ */
#include "dflat.h"

int BoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;
    CTLWINDOW *ct = GetControl(wnd);
    if (ct != NULL)    {
        switch (msg)    {
            case SETFOCUS:
            case PAINT:
                return FALSE;
            case LEFT_BUTTON:
            case BUTTON_RELEASED:
                return SendMessage(GetParent(wnd), msg, p1, p2);
            case BORDER:
                rtn = BaseWndProc(BOX, wnd, msg, p1, p2);
                if (ct != NULL)
                    if (ct->itext != NULL)
                        writeline(wnd, ct->itext, 1, 0, FALSE);
                return rtn;
            default:
                break;
        }
    }
    return BaseWndProc(BOX, wnd, msg, p1, p2);
}




<a name="019c_000c">
<a name="019c_000d">
[LISTING TWO]
<a name="019c_000d">

/* -------------- button.c -------------- */
#include "dflat.h"
void PaintMsg(WINDOW wnd, CTLWINDOW *ct, RECT *rc)
{
    if (isVisible(wnd))    {
        if (TestAttribute(wnd, SHADOW) && cfg.mono == 0)    {
            /* -------- draw the button's shadow ------- */
            int x;
            background = WndBackground(GetParent(wnd));
            foreground = BLACK;
            for (x = 1; x <= WindowWidth(wnd); x++)
                wputch(wnd, 223, x, 1);
            wputch(wnd, 220, WindowWidth(wnd), 0);
        }
        if (ct->itext != NULL)    {
            unsigned char *txt;
            txt = DFcalloc(1, strlen(ct->itext)+10);
            if (ct->setting == OFF)    {
                txt[0] = CHANGECOLOR;
                txt[1] = wnd->WindowColors
                            [HILITE_COLOR] [FG] | 0x80;
                txt[2] = wnd->WindowColors
                            [STD_COLOR] [BG] | 0x80;
            }
            CopyCommand(txt+strlen(txt),ct->itext,!ct->setting,
                WndBackground(wnd));
            SendMessage(wnd, CLEARTEXT, 0, 0);
            SendMessage(wnd, ADDTEXT, (PARAM) txt, 0);
            free(txt);
        }
        /* --------- write the button's text ------- */
        WriteTextLine(wnd, rc, 0, wnd == inFocus);
    }
}
void LeftButtonMsg(WINDOW wnd, MESSAGE msg, CTLWINDOW *ct)
{
    if (cfg.mono == 0)    {
        /* --------- draw a pushed button -------- */
        int x;
        background = WndBackground(GetParent(wnd));
        foreground = WndBackground(wnd);
        wputch(wnd, ' ', 0, 0);
        for (x = 0; x < WindowWidth(wnd); x++)    {
            wputch(wnd, 220, x+1, 0);
            wputch(wnd, 223, x+1, 1);
        }
    }
    if (msg == LEFT_BUTTON)
        SendMessage(NULL, WAITMOUSE, 0, 0);
    else
        SendMessage(NULL, WAITKEYBOARD, 0, 0);
    SendMessage(wnd, PAINT, 0, 0);
    if (ct->setting == ON)
        PostMessage(GetParent(wnd), COMMAND, ct->command, 0);
    else
        beep();
}
int ButtonProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    CTLWINDOW *ct = GetControl(wnd);
    if (ct != NULL)    {
        switch (msg)    {
            case SETFOCUS:
                BaseWndProc(BUTTON, wnd, msg, p1, p2);
                p1 = 0;
                /* ------- fall through ------- */
            case PAINT:
                PaintMsg(wnd, ct, (RECT*)p1);
                return TRUE;
            case KEYBOARD:
                if (p1 != '\r')
                    break;
                /* ---- fall through ---- */
            case LEFT_BUTTON:
                LeftButtonMsg(wnd, msg, ct);
                return TRUE;
            case HORIZSCROLL:
                return TRUE;
            default:
                break;
        }
    }
    return BaseWndProc(BUTTON, wnd, msg, p1, p2);
}




<a name="019c_000e">
<a name="019c_000f">
[LISTING THREE]
<a name="019c_000f">

/* -------------- checkbox.c ------------ */
#include "dflat.h"
int CheckBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;
    CTLWINDOW *ct = GetControl(wnd);
    if (ct != NULL)    {
        switch (msg)    {
            case SETFOCUS:
                if (!(int)p1)
                    SendMessage(NULL, HIDE_CURSOR, 0, 0);
            case MOVE:
                rtn = BaseWndProc(CHECKBOX, wnd, msg, p1, p2);
                SetFocusCursor(wnd);
                return rtn;
            case PAINT:    {
                char cb[] = "[ ]";
                if (ct->setting)
                    cb[1] = 'X';
                SendMessage(wnd, CLEARTEXT, 0, 0);
                SendMessage(wnd, ADDTEXT, (PARAM) cb, 0);
                SetFocusCursor(wnd);
                break;
            }
            case KEYBOARD:
                if ((int)p1 != ' ')
                    break;
            case LEFT_BUTTON:
                ct->setting ^= ON;
                SendMessage(wnd, PAINT, 0, 0);
                return TRUE;
            default:
                break;
        }
    }
    return BaseWndProc(CHECKBOX, wnd, msg, p1, p2);
}
BOOL CheckBoxSetting(DBOX *db, enum commands cmd)
{
    CTLWINDOW *ct = FindCommand(db, cmd, CHECKBOX);
    if (ct != NULL)
        return (ct->isetting == ON);
    return FALSE;
}




<a name="019c_0010">
<a name="019c_0011">
[LISTING FOUR]
<a name="019c_0011">

/* -------------- combobox.c -------------- */
#include "dflat.h"
int ListProc(WINDOW, MESSAGE, PARAM, PARAM);
int ComboProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    switch (msg)    {
        case CREATE_WINDOW:
            wnd->extension = CreateWindow(
                        LISTBOX,
                        NULL,
                        wnd->rc.lf,wnd->rc.tp+1,
                        wnd->ht-1, wnd->wd+1,
                        NULL,
                        GetParent(wnd),
                        ListProc,
                        HASBORDER | NOCLIP | SAVESELF);
            ((WINDOW)(wnd->extension))->ct->command =
                                        wnd->ct->command;
            wnd->ht = 1;
            wnd->rc.bt = wnd->rc.tp;
            break;
        case PAINT:
            foreground = FrameForeground(wnd);
            background = FrameBackground(wnd);
            wputch(wnd, DOWNSCROLLBOX, WindowWidth(wnd), 0);
            break;
        case KEYBOARD:
            if ((int)p1 == DN)    {
                SendMessage(wnd->extension, SETFOCUS, TRUE, 0);
                return TRUE;
            }
            break;
        case LEFT_BUTTON:
            if ((int)p1 == GetRight(wnd) + 1)
                SendMessage(wnd->extension, SETFOCUS, TRUE, 0);
            break;
        case CLOSE_WINDOW:
            SendMessage(wnd->extension, CLOSE_WINDOW, 0, 0);
            break;
        default:
            break;
    }
    return BaseWndProc(COMBOBOX, wnd, msg, p1, p2);
}
int ListProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    DBOX *db = GetParent(wnd)->extension;
    WINDOW cwnd = ControlWindow(db, wnd->ct->command);
    char text[130];
    int rtn;
    WINDOW currFocus;
    switch (msg)    {
        case CREATE_WINDOW:
            wnd->ct = DFmalloc(sizeof(CTLWINDOW));
            wnd->ct->setting = OFF;
            break;
        case SETFOCUS:
            if ((int)p1 == FALSE)    {
                SendMessage(wnd, HIDE_WINDOW, 0, 0);
                wnd->ct->setting = OFF;
            }
            else
                wnd->ct->setting = ON;
            break;
        case SHOW_WINDOW:
            if (wnd->ct->setting == OFF)
                return TRUE;
            break;
        case BORDER:
            currFocus = inFocus;
            inFocus = NULL;
            rtn = DefaultWndProc(wnd, msg, p1, p2);
            inFocus = currFocus;
            return rtn;
        case LB_SELECTION:
            rtn = DefaultWndProc(wnd, msg, p1, p2);
            SendMessage(wnd, LB_GETTEXT,
                            (PARAM) text, wnd->selection);
            PutItemText(GetParent(wnd), wnd->ct->command, text);
            SendMessage(cwnd, PAINT, 0, 0);
            cwnd->TextChanged = TRUE;
            return rtn;
        case KEYBOARD:
            switch ((int) p1)    {
                case ESC:
                case FWD:
                case BS:
                    SendMessage(cwnd, SETFOCUS, TRUE, 0);
                    return TRUE;
                default:
                    break;
            }
            break;
        case LB_CHOOSE:
            SendMessage(cwnd, SETFOCUS, TRUE, 0);
            return TRUE;
        case CLOSE_WINDOW:
            if (wnd->ct != NULL)
                free(wnd->ct);
            wnd->ct = NULL;
            break;
        default:
            break;
    }
    return DefaultWndProc(wnd, msg, p1, p2);
}
void PutComboListText(WINDOW wnd, enum commands cmd, char *text)
{
    CTLWINDOW *ct = FindCommand(wnd->extension, cmd, COMBOBOX);
    if (ct != NULL)        {
        WINDOW lwnd = ((WINDOW)(ct->wnd))->extension;
        SendMessage(lwnd, ADDTEXT, (PARAM) text, 0);
    }
}




<a name="019c_0012">
<a name="019c_0013">
[LISTING FIVE]
<a name="019c_0013">

/* ------------------ msgbox.c ------------------ */
#include "dflat.h"
extern DBOX MsgBox;
extern DBOX InputBoxDB;
WINDOW CancelWnd;
static int ReturnValue;
int MessageBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    switch (msg)    {
        case CREATE_WINDOW:
            GetClass(wnd) = MESSAGEBOX;
            ClearAttribute(wnd, CONTROLBOX);
            break;
        case KEYBOARD:
            if (p1 == '\r' || p1 == ESC)
                ReturnValue = (int)p1;
            break;
        default:
            break;
    }
    return BaseWndProc(MESSAGEBOX, wnd, msg, p1, p2);
}
int YesNoBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    switch (msg)    {
        case CREATE_WINDOW:
            GetClass(wnd) = MESSAGEBOX;
            ClearAttribute(wnd, CONTROLBOX);
            break;
        case KEYBOARD:    {
            int c = tolower((int)p1);
            if (c == 'y')
                SendMessage(wnd, COMMAND, ID_OK, 0);
            else if (c == 'n')
                SendMessage(wnd, COMMAND, ID_CANCEL, 0);
            break;
        }
        default:
            break;
    }
    return BaseWndProc(MESSAGEBOX, wnd, msg, p1, p2);
}
int ErrorBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    switch (msg)    {
        case CREATE_WINDOW:
            GetClass(wnd) = ERRORBOX;
            break;
        case KEYBOARD:
            if (p1 == '\r' || p1 == ESC)
                ReturnValue = (int)p1;
            break;
        default:
            break;
    }
    return BaseWndProc(ERRORBOX, wnd, msg, p1, p2);
}
int CancelBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    switch (msg)    {
        case CREATE_WINDOW:
            CancelWnd = wnd;
            SendMessage(wnd, CAPTURE_MOUSE, 0, 0);
            SendMessage(wnd, CAPTURE_KEYBOARD, 0, 0);
            break;
        case COMMAND:
            if ((int) p1 == ID_CANCEL && (int) p2 == 0)
                SendMessage(GetParent(wnd), msg, p1, p2);
            return TRUE;
        case CLOSE_WINDOW:
            CancelWnd = NULL;
            SendMessage(wnd, RELEASE_MOUSE, 0, 0);
            SendMessage(wnd, RELEASE_KEYBOARD, 0, 0);
            p1 = TRUE;
            break;
        default:
            break;
    }
    return BaseWndProc(MESSAGEBOX, wnd, msg, p1, p2);
}
void CloseCancelBox(void)
{
    if (CancelWnd != NULL)
        SendMessage(CancelWnd, CLOSE_WINDOW, 0, 0);
}
static char *InputText;
static int TextLength;
int InputBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;
    switch (msg)    {
        case CREATE_WINDOW:
            rtn = DefaultWndProc(wnd, msg, p1, p2);
            SendMessage(ControlWindow(&InputBoxDB,ID_INPUTTEXT),
                        SETTEXTLENGTH, TextLength, 0);
            return rtn;
        case COMMAND:
            if ((int) p1 == ID_OK && (int) p2 == 0)
                GetItemText(wnd, ID_INPUTTEXT, InputText, TextLength);
            break;
        default:
            break;
    }
    return DefaultWndProc(wnd, msg, p1, p2);
}
BOOL InputBox(WINDOW wnd,char *ttl,char *msg,char *text,int len)
{
    InputText = text;
    TextLength = len;
    InputBoxDB.dwnd.title = ttl;
    InputBoxDB.dwnd.w = 4 +
        max(20, max(len, max(strlen(ttl), strlen(msg))));
    InputBoxDB.ctl[1].dwnd.x = (InputBoxDB.dwnd.w-2-len)/2;
    InputBoxDB.ctl[0].dwnd.w = strlen(msg);
    InputBoxDB.ctl[0].itext = msg;
    InputBoxDB.ctl[1].dwnd.w = len;
    InputBoxDB.ctl[2].dwnd.x = (InputBoxDB.dwnd.w - 20) / 2;
    InputBoxDB.ctl[3].dwnd.x = InputBoxDB.ctl[2].dwnd.x + 10;
    InputBoxDB.ctl[2].isetting = ON;
    InputBoxDB.ctl[3].isetting = ON;
    return DialogBox(wnd, &InputBoxDB, TRUE, InputBoxProc);
}

BOOL GenericMessage(WINDOW wnd,char *ttl,char *msg,int buttonct,
      int (*wndproc)(struct window *,enum messages,PARAM,PARAM),
      char *b1, char *b2, int c1, int c2, int isModal)
{
    BOOL rtn;
    MsgBox.dwnd.title = ttl;
    MsgBox.ctl[0].dwnd.h = MsgHeight(msg);
    MsgBox.ctl[0].dwnd.w = max(max(MsgWidth(msg),
            buttonct*8 + buttonct + 2), strlen(ttl)+2);
    MsgBox.dwnd.h = MsgBox.ctl[0].dwnd.h+6;
    MsgBox.dwnd.w = MsgBox.ctl[0].dwnd.w+4;
    if (buttonct == 1)
        MsgBox.ctl[1].dwnd.x = (MsgBox.dwnd.w - 10) / 2;
    else    {
        MsgBox.ctl[1].dwnd.x = (MsgBox.dwnd.w - 20) / 2;
        MsgBox.ctl[2].dwnd.x = MsgBox.ctl[1].dwnd.x + 10;
        MsgBox.ctl[2].class = BUTTON;
    }
    MsgBox.ctl[1].dwnd.y = MsgBox.dwnd.h - 4;
    MsgBox.ctl[2].dwnd.y = MsgBox.dwnd.h - 4;
    MsgBox.ctl[0].itext = msg;
    MsgBox.ctl[1].itext = b1;
    MsgBox.ctl[2].itext = b2;
    MsgBox.ctl[1].command = c1;
    MsgBox.ctl[2].command = c2;
    MsgBox.ctl[1].isetting = ON;
    MsgBox.ctl[2].isetting = ON;
    rtn = DialogBox(wnd, &MsgBox, isModal, wndproc);
    MsgBox.ctl[2].class = 0;
    return rtn;
}
WINDOW MomentaryMessage(char *msg)
{
    WINDOW wnd = CreateWindow(
                    TEXTBOX,
                    NULL,
                    -1,-1,MsgHeight(msg)+2,MsgWidth(msg)+2,
                    NULL,NULL,NULL,
                    HASBORDER | SHADOW | SAVESELF);
    SendMessage(wnd, SETTEXT, (PARAM) msg, 0);
    if (cfg.mono == 0)    {
        WindowClientColor(wnd, WHITE, GREEN);
        WindowFrameColor(wnd, WHITE, GREEN);
    }
    SendMessage(wnd, SHOW_WINDOW, 0, 0);
    return wnd;
}
int MsgHeight(char *msg)
{
    int h = 1;
    while ((msg = strchr(msg, '\n')) != NULL)    {
        h++;
        msg++;
    }
    return min(h, SCREENHEIGHT-10);
}
int MsgWidth(char *msg)
{
    int w = 0;
    char *cp = msg;
    while ((cp = strchr(msg, '\n')) != NULL)    {
        w = max(w, (int) (cp-msg));
        msg = cp+1;
    }
    return min(max(strlen(msg),w), SCREENWIDTH-10);
}




<a name="019c_0014">
<a name="019c_0015">
[LISTING SIX]
<a name="019c_0015">

/* -------- radio.c -------- */
#include "dflat.h"
static CTLWINDOW *rct[MAXRADIOS];
int RadioButtonProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;
    DBOX *db = GetParent(wnd)->extension;
    CTLWINDOW *ct = GetControl(wnd);
    if (ct != NULL)    {
        switch (msg)    {
            case SETFOCUS:
                if (!(int)p1)
                    SendMessage(NULL, HIDE_CURSOR, 0, 0);
            case MOVE:
                rtn = BaseWndProc(RADIOBUTTON,wnd,msg,p1,p2);
                SetFocusCursor(wnd);
                return rtn;
            case PAINT:    {
                char rb[] = "( )";
                if (ct->setting)
                    rb[1] = 7;
                SendMessage(wnd, CLEARTEXT, 0, 0);
                SendMessage(wnd, ADDTEXT, (PARAM) rb, 0);
                SetFocusCursor(wnd);
                break;
            }
            case KEYBOARD:
                if ((int)p1 != ' ')
                    break;
            case LEFT_BUTTON:
                PushRadioButton(db, ct->command);
                break;
            default:
                break;
        }
    }
    return BaseWndProc(RADIOBUTTON, wnd, msg, p1, p2);
}
void PushRadioButton(DBOX *db, enum commands cmd)
{
    CTLWINDOW *ct = FindCommand(db, cmd, RADIOBUTTON);
    if (ct != NULL)    {
        SetRadioButton(db, ct);
        ct->isetting = ON;
    }
}
void SetRadioButton(DBOX *db, CTLWINDOW *ct)
{
    CTLWINDOW *ctt = db->ctl;
    int i;
    /* --- clear all the radio buttons in this group on the dialog box --- */
    /* -------- build a table of all radio buttons at the
            same x vector ---------- */
    for (i = 0; i < MAXRADIOS; i++)
        rct[i] = NULL;
    while (ctt->class)    {
        if (ctt->class == RADIOBUTTON)
            if (ct->dwnd.x == ctt->dwnd.x)
                rct[ctt->dwnd.y] = ctt;
        ctt++;
    }
    /* ----- find the start of the radiobutton group ---- */
    i = ct->dwnd.y;
    while (i >= 0 && rct[i] != NULL)
        --i;
    /* ---- ignore everthing before the group ------ */
    while (i >= 0)
        rct[i--] = NULL;
    /* ----- find the end of the radiobutton group ---- */
    i = ct->dwnd.y;
    while (i < MAXRADIOS && rct[i] != NULL)
        i++;
    /* ---- ignore everthing past the group ------ */
    while (i < MAXRADIOS)
        rct[i++] = NULL;
    for (i = 0; i < MAXRADIOS; i++)    {
        if (rct[i] != NULL)    {
            int wason = rct[i]->setting;
            rct[i]->setting = OFF;
            if (wason)
                SendMessage(rct[i]->wnd, PAINT, 0, 0);
        }
    }
    ct->setting = ON;
    SendMessage(ct->wnd, PAINT, 0, 0);
}
BOOL RadioButtonSetting(DBOX *db, enum commands cmd)
{
    CTLWINDOW *ct = FindCommand(db, cmd, RADIOBUTTON);
    if (ct != NULL)
        return (ct->setting == ON);
    return FALSE;
}




<a name="019c_0016">
<a name="019c_0017">
[LISTING SEVEN]
<a name="019c_0017">

/* ------------- slidebox.c ------------ */
#include "dflat.h"
static int (*GenericProc)
    (WINDOW wnd,MESSAGE msg,PARAM p1,PARAM p2);
static BOOL KeepRunning;
static int SliderLen;
static int Percent;
extern DBOX SliderBoxDB;
static void InsertPercent(char *s)
{
    int offset;
    char pcc[5];

    sprintf(s, "%c%c%c",
            CHANGECOLOR,
            color[DIALOG][SELECT_COLOR][FG]+0x80,
            color[DIALOG][SELECT_COLOR][BG]+0x80);
    s += 3;
    memset(s, ' ', SliderLen);
    *(s+SliderLen) = '\0';
    sprintf(pcc, "%d%%", Percent);
    strncpy(s+SliderLen/2-1, pcc, strlen(pcc));
    offset = (SliderLen * Percent) / 100;
    memmove(s+offset+4, s+offset, strlen(s+offset)+1);
    sprintf(pcc, "%c%c%c%c",
            RESETCOLOR,
            CHANGECOLOR,
            color[DIALOG][SELECT_COLOR][BG]+0x80,
            color[DIALOG][SELECT_COLOR][FG]+0x80);
    strncpy(s+offset, pcc, 4);
    *(s + strlen(s) - 1) = RESETCOLOR;
}
static int SliderTextProc(
            WINDOW wnd,MESSAGE msg,PARAM p1,PARAM p2)
{
    switch (msg)    {
        case PAINT:
            Percent = (int)p2;
            InsertPercent(GetText(wnd) ?
                GetText(wnd) : SliderBoxDB.ctl[1].itext);
            GenericProc(wnd, PAINT, 0, 0);
            if (Percent >= 100)
                SendMessage(GetParent(wnd),COMMAND,ID_CANCEL,0);
            if (!dispatch_message())
                PostMessage(GetParent(wnd), ENDDIALOG, 0, 0);
            return KeepRunning;
        default:
            break;
    }
    return GenericProc(wnd, msg, p1, p2);
}
static int SliderBoxProc(
            WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;
    WINDOW twnd;
    switch (msg)    {
        case CREATE_WINDOW:
            AddAttribute(wnd, SAVESELF);
            rtn = DefaultWndProc(wnd, msg, p1, p2);
            twnd = SliderBoxDB.ctl[1].wnd;
            GenericProc = twnd->wndproc;
            twnd->wndproc = SliderTextProc;
            KeepRunning = TRUE;
            SendMessage(wnd, CAPTURE_MOUSE, 0, 0);
            SendMessage(wnd, CAPTURE_KEYBOARD, 0, 0);
            return rtn;
        case COMMAND:
            if ((int)p2 == 0 && (int)p1 == ID_CANCEL)    {
                if (Percent >= 100 ||
                        YesNoBox("Terminate process?"))
                    KeepRunning = FALSE;
                else
                    return TRUE;
            }
            break;
        case CLOSE_WINDOW:
            SendMessage(wnd, RELEASE_MOUSE, 0, 0);
            SendMessage(wnd, RELEASE_KEYBOARD, 0, 0);
            break;
        default:
            break;
    }
    return DefaultWndProc(wnd, msg, p1, p2);
}
WINDOW SliderBox(int len, char *ttl, char *msg)
{
    SliderLen = len;
    SliderBoxDB.dwnd.title = ttl;
    SliderBoxDB.dwnd.w =
        max(strlen(ttl),max(len, strlen(msg)))+4;
    SliderBoxDB.ctl[0].itext = msg;
    SliderBoxDB.ctl[0].dwnd.w = strlen(msg);
    SliderBoxDB.ctl[0].dwnd.x =
        (SliderBoxDB.dwnd.w - strlen(msg)-1) / 2;
    SliderBoxDB.ctl[1].itext =
        DFrealloc(SliderBoxDB.ctl[1].itext, len+10);
    Percent = 0;
    InsertPercent(SliderBoxDB.ctl[1].itext);
    SliderBoxDB.ctl[1].dwnd.w = len;
    SliderBoxDB.ctl[1].dwnd.x = (SliderBoxDB.dwnd.w-len-1)/2;
    SliderBoxDB.ctl[2].dwnd.x = (SliderBoxDB.dwnd.w-10)/2;
    DialogBox(NULL, &SliderBoxDB, FALSE, SliderBoxProc);
    return SliderBoxDB.ctl[1].wnd;
}




<a name="019c_0018">
<a name="019c_0019">
[LISTING EIGHT]
<a name="019c_0019">

/* ------------ spinbutt.c ------------- */
#include "dflat.h"
int SpinButtonProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;
    CTLWINDOW *ct = GetControl(wnd);
    if (ct != NULL)    {
        switch (msg)    {
            case CREATE_WINDOW:
                wnd->wd -= 2;
                wnd->rc.rt -= 2;
                break;
            case SETFOCUS:
                rtn = BaseWndProc(SPINBUTTON, wnd, msg, p1, p2);
                if (!(int)p1)
                    SendMessage(NULL, HIDE_CURSOR, 0, 0);
                SetFocusCursor(wnd);
                return rtn;
            case PAINT:
                foreground = FrameForeground(wnd);
                background = FrameBackground(wnd);
                wputch(wnd,UPSCROLLBOX,WindowWidth(wnd), 0);
                wputch(wnd,DOWNSCROLLBOX,WindowWidth(wnd)+1,0);
                SetFocusCursor(wnd);
                break;
            case LEFT_BUTTON:
                if (p1 == GetRight(wnd) + 1)
                    SendMessage(wnd, KEYBOARD, UP, 0);
                else if (p1 == GetRight(wnd) + 2)
                    SendMessage(wnd, KEYBOARD, DN, 0);
                if (wnd != inFocus)
                    SendMessage(wnd, SETFOCUS, TRUE, 0);
                return TRUE;
            case LB_SETSELECTION:
                rtn = BaseWndProc(SPINBUTTON, wnd, msg, p1, p2);
                wnd->wtop = (int) p1;
                SendMessage(wnd, PAINT, 0, 0);
                return rtn;
            default:
                break;
        }
    }
    return BaseWndProc(SPINBUTTON, wnd, msg, p1, p2);
}





<a name="019c_001a">
<a name="019c_001b">
[LISTING NINE]
<a name="019c_001b">

/* -------------- text.c -------------- */
#include "dflat.h"
int TextProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int i, len;
    CTLWINDOW *ct = GetControl(wnd);
    char *cp, *cp2 = ct->itext;
    switch (msg)    {
        case PAINT:
            if (ct == NULL ||
                ct->itext == NULL ||
                    GetText(wnd) != NULL)
                break;
            len = min(ct->dwnd.h, MsgHeight(cp2));
            cp = cp2;
            for (i = 0; i < len; i++)    {
                int mlen;
                char *txt = cp;
                char *cp1 = cp;
                char *np = strchr(cp, '\n');
                if (np != NULL)
                    *np = '\0';
                mlen = strlen(cp);
                while ((cp1=strchr(cp1,SHORTCUTCHAR)) != NULL) {
                    mlen += 3;
                    cp1++;
                }
                if (np != NULL)
                    *np = '\n';
                txt = DFmalloc(mlen+1);
                 CopyCommand(txt, cp, FALSE, WndBackground(wnd));
                txt[mlen] = '\0';
                SendMessage(wnd, ADDTEXT, (PARAM)txt, 0);
                if ((cp = strchr(cp, '\n')) != NULL)
                    cp++;
                free(txt);
            }
            break;
        default:
            break;
    }
    return BaseWndProc(TEXT, wnd, msg, p1, p2);
}




<a name="019c_001c">
<a name="019c_001d">
[LISTING TEN]
<a name="019c_001d">

/* ----------- watch.c ----------- */

#include "dflat.h"

static int WatchIconProc(
                WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;
    switch (msg)    {
        case CREATE_WINDOW:
            rtn = DefaultWndProc(wnd, msg, p1, p2);
            SendMessage(wnd, CAPTURE_MOUSE, 0, 0);
            SendMessage(wnd, HIDE_MOUSE, 0, 0);
            SendMessage(wnd, CAPTURE_KEYBOARD, 0, 0);
            return rtn;
        case PAINT:
            SetStandardColor(wnd);
            writeline(wnd, " @ ", 1, 1, FALSE);
            return TRUE;
        case BORDER:
            rtn = DefaultWndProc(wnd, msg, p1, p2);
            writeline(wnd, "M", 2, 0, FALSE);
            return rtn;
        case MOUSE_MOVED:
            SendMessage(wnd, HIDE_WINDOW, TRUE, 0);
            SendMessage(wnd, MOVE, p1, p2);
            SendMessage(wnd, SHOW_WINDOW, 0, 0);
            return TRUE;
        case CLOSE_WINDOW:
            SendMessage(wnd, RELEASE_MOUSE, 0, 0);
            SendMessage(wnd, RELEASE_KEYBOARD, 0, 0);
            SendMessage(wnd, SHOW_MOUSE, 0, 0);
            break;
        default:
            break;
    }
    return DefaultWndProc(wnd, msg, p1, p2);
}

WINDOW WatchIcon(void)
{
    int mx, my;
    WINDOW wnd;
    SendMessage(NULL, CURRENT_MOUSE_CURSOR,
                        (PARAM) &mx, (PARAM) &my);
    wnd = CreateWindow(
                    BOX,
                    NULL,
                    mx, my, 3, 5,
                    NULL,NULL,
                    WatchIconProc,
                    VISIBLE | HASBORDER | SHADOW | SAVESELF);
    return wnd;
}


Copyright © 1992, 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.