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


JUN92: C PROGRAMMING

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

I spent a week in February in Santa Clara attending Software Development '92. This programmer's trade show gets bigger every year, and the breadth of its coverage provides a measure of where we are going and how we are going to get there. The emphasis this year was on Windows--not by design of the show's promoters but by virtue of the overwhelming number of Windows tools on display. Everywhere you looked there was another new programmer's tool for writing Windows code. Microsoft announced their C/C++ compiler with the Microsoft Foundation Class Library, which puts the Windows SDK in an object-oriented C++ wrapper. Instantiate an object, and a window opens. Send it a message, and it does something. Go out of scope, and the window closes. This is Microsoft's answer to Borland's OWL. Those guys keep answering each other. More about both later on.

These shows are where you meet the folks who wear the faces that you can attach to the names that you've been reading in magazines and on CompuServe. As you walk around the show and talk to people, they always stare at your chest first. This is to read your attendee's badge to see if they've heard of you, but now I know how Dolly Parton must feel.

The show included several exhibitors demonstrating some new "virtual reality" devices. I'm not sure what that had to do with programming, but it looked like it could be fun. People were sticking their heads into Darth Vader helmets and jerking joy sticks and their heads around. They were apparently simulating movement through space in three dimensions and seeing it all in the viewers of those helmets. It looked like it could have serious application in flight simulators and such. I tried one on. I was not particularly impressed with the simulation, but this stuff is new. Then Maggie Dunphy of Innovative Data Concepts expressed concern about head lice. "You never know who's been wearing that thing in a public show like this!" She was looking at some passing souls whose grooming, coiffure, and accoutrements recall the early days of personal computing--or is it just the recession? My kind of programmer, anyway. I hastily took the helmet off and didn't try on any of the others. We all went to the bar and had some California wheat beer. I scratched my scalp from time to time. Couldn't help it.

P.J. Plauger presented two sessions at SD '92. One was on how not to develop "shelfware"--software destined for the shelf because the user will not use it. The other presentation was about a C++ function library that Dr. Plauger is designing to follow his book, The Standard C Library (Prentice Hall, 1992). He is, as usual, taking the pragmatist's view. He won't be trying to force-feed us some pure, all-encompassing, object-oriented class library that defines the world and all its entrails and appendages. Instead, he promises to address those areas where the features of C++ can be used to provide improvements to the functions of standard C. For example, the language wouldn't need strcpy, strncpy, and every other distinct string-moving function. By overloading a single function, the library would give us just one to remember. Its behavior would depend on which data types you wanted to move. And, for purposes of compatibility with the past, the C++ library would include all the standard C library functions. Something to look forward to. Here's something else. Dr. Plauger told me that he and Brian Kernighan are working on a C version of their book, Elements of Programming Style.

The Free Software Foundation had a booth, and Richard Stallman gave me a button that said "Keep your lawyers out of my computer." He said I could have it only if I would wear it and if I knew what it meant. I said that I would and I did, and then he asked for a donation to help defray his expenses. For a second there I had to look around to make sure I wasn't at the airport. I gave him a couple of bucks and wore the button because I agree with its sentiment. It's logo is a snake with an apple for a head.

Finally, Liz Oakley came out of the closet. That doesn't mean what it usually means, but I'm not telling.

D-Flat Dialog Boxes

A dialog box is a window that has one or more control windows with which the user enters commands and data. In the September 1991 column I described how you define your dialog boxes with macros. Last month we discussed the APPLICATION window class, and you saw how the program uses dialog boxes. This month we will look at the code that implements the dialog box itself. Next month we will look at the code that implements the control windows.

Listing One, page 148, is dialbox.c. It includes the DialogBox function that an application calls to open a dialog box and the code to build and manage the dialog box. If a dialog box is "modal," it keeps the focus contained within itself while it is open. You cannot get out to a menu item or a document window without closing the dialog box. If a dialog box is "modeless," it works like any other document window. You open them both by calling the same function. The DialogBox function for a modeless dialog box returns as soon as it has created and displayed the dialog box. The function for a modal dialog box does not return until the user closes the dialog box.

The first function in dialbox.c is the ClearDialogBoxes function. The text-control windows in a dialog box retain the text values that the user enters even after the window closes. This allows the text entries to persist for subsequent uses of the dialog box. These text values are on the far heap. Usually DOS frees any heap allocations when the program terminates, but I added this function so that I could use D-Flat in TSR applications. D-Flat calls the ClearDialogBoxes function when the application is done, and the function frees all heap allocations that are still open.

The DialogBox function is the one that the application calls to open a dialog box. The function creates the dialog-box window, displays it, sets the focus to its first control window, and sends the dialog-box window the INITIATE_DIALOG message. If the dialog box is modeless, the function returns. If the dialog box is modal, the function captures the mouse and keyboard into the dialog-box window and enters the D-Flat message dispatching loop. The loop will not return until the dialog box gets closed, which will happen as the result of the actions taken in the dialog box's default window-processing module.

Dialog-box Messages

The dialog box can have a custom window-processing module, which the application provides. That is where its commands will be intercepted and processed. The default window-processing module in dialbox.c is DialogProc. It intercepts the messages and processes them.

The CREATE_WINDOW message builds the table of dialog boxes that the ClearDialogBox function will use. Then it chains to the window-processing module of the base window class to create the window. A dialog box has a number of control windows. This message then creates each of the control windows and initializes the text-based controls with the text that the dialog-box definition specifies.

The SETFOCUS message bypasses the usual D-Flat logic when setting the focus to a modal dialog box. It sends a message to clear the focus from whatever window currently has the focus and sets the inFocus variable.

Modal dialog boxes ignore the SHIFT_CHANGED message to prevent D-Flat from trying to give the focus to the menu bar when the user presses and releases the Alt key.

The dialog box processes the intercepted mouse LEFT_BUTTON message for combo-box and spin-button controls. If the mouse hits the scroll buttons for those controls, the program sends the message to the control windows themselves.

If the user presses F1, the KEY-BOARD message displays the help window associated with whichever control has the focus. The Shift+Tab, Backspace, and Up keys give the focus to the control that precedes the one with the current focus, while the Alt+F6, Tab, right-arrow, and down-arrow keys give the focus to the control that follows the current one. The Ctrl+F4 and Esc keys send the ID_CANCEL command to the dialog box. All other keystrokes are matched against the shortcut keys for the dialog box to see if the user is trying to set the focus to one of them.

If the user chooses the OK or Cancel commands, the COMMAND message sets a global variable to indicate which one, and posts the END_DIALOG message to a modal dialog box or sends the CLOSE_WINDOW message to a modeless dialog box.

The CLOSE_WINDOW message sends the ID_CANCEL command to the dialog box before closing the window.

Other Dialog-box Functions

Several other dialog-box functions support the control windows or the application. The FindCommand function returns the control structure for the control that contains the specified command code. The ControlWindow function returns the window handle of a specified control. Some controls, such as check boxes, are either on or off. The ControlSetting function sets the state of those controls. There are several functions that get and set the text values for dialog-box controls.

Control Window Messages

The ControlProc function is a generic window-processing module for all control windows. Each class of control window will have its own window-processing module, but the ControlProc function gets first shot at the messages for all controls.

The CREATE_WINDOW message moves the address of the control structure from the extension field to its own field in the window structure.

If the user presses F1, the KEYBOARD message displays the help window associated with the control. Alt+F6, Ctrl +F4, and Alt+F4 are posted to the dialog-box window. The arrow keys are converted to the keys that change the focus to adjacent controls, unless the control is an edit box or list box. The Enter key is converted to the ID_OK command except for multiline edit boxes and buttons. The PAINT message adds scroll bars from text windows if the width or length of the text exceeds the dimensions of the windows. The BORDER message prevents edit boxes from displaying the double-line border when they get the focus. The SET-FOCUS message sends the ENTERFOCUS or LEAVEFOCUS commands to the dialog box. The CLOSE_WINDOW command updates the initializing text values of text-based control windows if the user chose the OK command.

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 "careware" program. If you want to discuss D-Flat with me, use CompuServe. My ID is 71101,1262, and I monitor the DDJ Forum daily.

Some D-Flat++ Design Issues

I am brainstorming the architecture of what I will call D-Flat++, or DF++, until I come up with a better name. There are a number of design considerations to ponder. First among them are my objectives for DF++, which are:

  • A C++ class library that implements the D-Flat user interface
  • Code that compiles with Borland, Microsoft, "TopSpeed, and Zortech C++ compilers
An object-oriented class library for such a system will have many things in common with an event-driven, message-based architecture, and it will have many things that are different, too. I have a reasonable early idea of how the API will look. A program will declare a window variable, and the window will come into view. The program will modify the window's appearance by sending it messages and its behavior by adding new messages. How will the program do that?

Processing Messages

In the D-Flat API, a program provides a message-processing function when it creates a window. The message-processing function intercepts the messages that it wants to process and passes the others to the next message-processing function up the class tree. This procedure resembles polymorphism, but the language is not implementing it; the programmer is.

In a C++ class hierarchy, a base class can declare a virtual member function. When a derived class declares a member function with the same name and parameter list, the derived class function executes when the program calls the function in the name of an object of the derived class. Let's keep this terminology straight. Calling member functions is how a C++ program sends a message to the object. It means the same thing.

So, it would follow that the way to implement a window class that has its own messages is to derive a class and add some message functions to it. Remember that the class at the bottom of the hierarchy gets first whack at every message, so every message function must be virtual to support subsequent derived classes.

There is, however, a wrinkle. In the message-based architecture of D-Flat, a particular window class does not need to know about all the messages--only those it intercepts. It passes all others up the hierarchy. It can do that because a message is a generic data construct, and its meaning is interpreted by the class functions that process it. A message in C++ is a function. Assume a class hierarchy of A->B->C, with A as the topmost base class. If A and C both process a particular message, about which B is ignorant, then C must know to pass the message to A when C is done with it. That means that when you design a derived class, you must know a lot about the classes up the organization, which flies at Mach 1 straight into the face of the traditional concept of the abstract black box. The bright side is that the object-oriented approach eliminates a severe level of overhead. Every D-Flat message goes through the message-processing function of every class up the tree until one of them intercepts it and processes it. The DF++ message approach will send the message to the lowest function in the tree that is interested in it. The lower class functions will never see it. If that function needs to pass the message up, the message will go directly to the next interested class. Whether or not the virtual-table mechanism of C++ imposes a more severe overhead than the one we will eliminate remains to be seen. One thing is for sure, though. You'll spend less time in your debugger stepping through layers of message-processing functions that drop out the bottom of a big switch statement, only to pass the message on to the next class.

Identifying Messages

In D-Flat, a message has an enumerated value. In C++, a message has a function name. One of the D-Flat messages is COMMAND, which menus and dialog boxes send to say that the user has chosen a menu item, pressed a command button, and so on. Each discrete command has an enumerated value, too. When you design a menu or dialog box, you associate the command value with the user's action. The application or dialog window has a unique message-processing function that intercepts the COMMAND message, figures out what command action occurred, and executes some code to respond to the command.

Ideally, a window class would have a member function for each command, and the design of a menu or dialog box would associate that member function with the user's action. The D-Flat API is similar to the Windows API in many ways, so it would be helpful to see how some of the C++ class libraries for Windows do it.

The Foundation. The Microsoft Foundation Class Library uses what they call a "message map." They retain the Windows messages and command codes, and they associate them in the message map with macro statements such as this:

ON_COMMAND(IDM_NEW, OnNew)

The OnNew parameter in that macro names the member function that represents the COMMAND message for the IDM_NEW command.

OWL. The Borland ObjectWindows class library takes a different approach. Borland has extended the syntax of the C++ language to specify the declaration of a "message-response member function." The declaration is a public member function of the class that looks like this:

  virtual void CMFileNew(RtMessage Msg) = [CM_FIRST+CM_FILENEW];

C++/Views. The C++/Views class library from CNS uses yet another approach. An application program instantiates a pop-down menu and sends it a message with a pointer to an array of structures--one for each item on the menu. The structure includes a reference to the application window and a function pointer to call when the user chooses the menu item.

Remember, however, that all these class libraries are C++ wrappers around the event-driven, message-based Windows API, and so they necessarily retain some of the characteristics of that platform. The DF++ objective is to rewrite the underlying user-interface software as well as its API in C++, so we are not bound to any existing methods and architecture. We'll see how it goes.

Other Classes

There are no standards for C++ classes. Stroustrup ignored the issue. The only de facto standard is the IOSTREAM package that AT&T distributes and that most compilers include. Yet there are some obvious needs. Many compilers include classes for strings, containers, and such, but there is no consensus on format and implementation. Some will disagree, but, in my opinion, the single most valuable improvement that C++ brings to C is the ability for a programmer to extend the language by adding data types. There are other improvements, of course, but if you never used any of them and if you never wrote an object-oriented program, this one feature would make C++ worth using. But where are the classes? The DF++ project begins with no standard library except the C standard library, and, therefore, no standard classes, and I must decide what to do about that. One option would be to follow Bill Plauger's progress in that area. Your thoughts would be appreciated.



_C PROGRAMMING COLUMN_
by Al Stevens


[LISTING ONE]
<a name="0163_000e">

/* ----------------- dialbox.c -------------- */

#include "dflat.h"

static int inFocusCommand(DBOX *);
static void dbShortcutKeys(DBOX *, int);
static int ControlProc(WINDOW, MESSAGE, PARAM, PARAM);
static void ChangeFocus(WINDOW, int);
static CTLWINDOW *AssociatedControl(DBOX *, enum commands);

static BOOL SysMenuOpen;

static DBOX **dbs = NULL;
static int dbct = 0;

/* --- clear all heap allocations to control text fields --- */
void ClearDialogBoxes(void)
{
    int i;
    for (i = 0; i < dbct; i++)    {
        CTLWINDOW *ct = (*(dbs+i))->ctl;
        while (ct->class)    {
            if ((ct->class == EDITBOX ||
                    ct->class == COMBOBOX) &&
                    ct->itext != NULL)
                free(ct->itext);
            ct++;
        }
    }
    if (dbs != NULL)    {
        free(dbs);
        dbs = NULL;
    }
    dbct = 0;
}

/* -------- CREATE_WINDOW Message --------- */
static int CreateWindowMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    DBOX *db = wnd->extension;
    CTLWINDOW *ct = db->ctl;
    WINDOW cwnd;
    int rtn, i;
    /* ---- build a table of processed dialog boxes ---- */
    for (i = 0; i < dbct; i++)
        if (db == dbs[i])
            break;
    if (i == dbct)    {
        dbs = realloc(dbs, sizeof(DBOX *) * (dbct+1));
        if (dbs != NULL)
            *(dbs + dbct++) = db;
    }
    rtn = BaseWndProc(DIALOG, wnd, CREATE_WINDOW, p1, p2);
    ct = db->ctl;
    while (ct->class)    {
        int attrib = 0;
        if (TestAttribute(wnd, NOCLIP))
            attrib |= NOCLIP;
        if (wnd->Modal)
            attrib |= SAVESELF;
        ct->setting = ct->isetting;
        if (ct->class == EDITBOX && ct->dwnd.h > 1)
            attrib |= (MULTILINE | HASBORDER);
        else if (ct->class == LISTBOX || ct->class == TEXTBOX)
            attrib |= HASBORDER;
        cwnd = CreateWindow(ct->class,
                        ct->dwnd.title,
                        ct->dwnd.x+GetClientLeft(wnd),
                        ct->dwnd.y+GetClientTop(wnd),
                        ct->dwnd.h,
                        ct->dwnd.w,
                        ct,
                        wnd,
                        ControlProc,
                        attrib);
        if ((ct->class == EDITBOX ||
                ct->class == COMBOBOX) &&
                    ct->itext != NULL)
            SendMessage(cwnd, SETTEXT, (PARAM) ct->itext, 0);
        if (ct->class != BOX &&
            ct->class != TEXT &&
                wnd->dFocus == NULL)
            wnd->dFocus = ct;
        ct++;
    }
    return rtn;
}

/* -------- LEFT_BUTTON Message --------- */
static BOOL LeftButtonMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    DBOX *db = wnd->extension;
    CTLWINDOW *ct = db->ctl;
    if (WindowSizing || WindowMoving)
        return TRUE;
    if (HitControlBox(wnd, p1-GetLeft(wnd), p2-GetTop(wnd))) {
        PostMessage(wnd, KEYBOARD, ' ', ALTKEY);
        return TRUE;
    }
    while (ct->class)    {
        WINDOW cwnd = ct->wnd;
        if (ct->class == COMBOBOX)    {
            if (p2 == GetTop(cwnd))    {
                if (p1 == GetRight(cwnd)+1)    {
                    SendMessage(cwnd, LEFT_BUTTON, p1, p2);
                    return TRUE;
                }
            }
            if (GetClass(inFocus) == LISTBOX)
                SendMessage(wnd, SETFOCUS, TRUE, 0);
        }
        else if (ct->class == SPINBUTTON)    {
            if (p2 == GetTop(cwnd))    {
                if (p1 == GetRight(cwnd)+1 ||
                        p1 == GetRight(cwnd)+2)    {
                    SendMessage(cwnd, LEFT_BUTTON, p1, p2);
                    return TRUE;
                }
            }
        }
        ct++;
    }
    return FALSE;
}

/* -------- KEYBOARD Message --------- */
static BOOL KeyboardMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    DBOX *db = wnd->extension;
    CTLWINDOW *ct = db->ctl;

    if (WindowMoving || WindowSizing)
        return FALSE;
    switch ((int)p1)    {
        case F1:
            ct = wnd->dFocus;
            if (ct != NULL)
                if (DisplayHelp(wnd, ct->help))
                    return TRUE;
            break;
        case SHIFT_HT:
        case BS:
        case UP:
            ChangeFocus(wnd, FALSE);
            break;
        case ALT_F6:
        case '\t':
        case FWD:
        case DN:
            ChangeFocus(wnd, TRUE);
            break;
        case ' ':
            if (((int)p2 & ALTKEY) &&
                    TestAttribute(wnd, CONTROLBOX))    {
                SysMenuOpen = TRUE;
                BuildSystemMenu(wnd);
            }
            break;
        case CTRL_F4:
        case ESC:
            SendMessage(wnd, COMMAND, ID_CANCEL, 0);
            break;
        default:
            /* ------ search all the shortcut keys ----- */
            dbShortcutKeys(db, (int) p1);
            break;
    }
    return wnd->Modal;
}

/* -------- COMMAND Message --------- */
static BOOL CommandMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    DBOX *db = wnd->extension;
    switch ((int) p1)    {
        case ID_OK:
        case ID_CANCEL:
            if ((int)p2 != 0)
                return TRUE;
            wnd->ReturnCode = (int) p1;
            if (wnd->Modal)
                PostMessage(wnd, ENDDIALOG, 0, 0);
            else
                SendMessage(wnd, CLOSE_WINDOW, TRUE, 0);
            return TRUE;
        case ID_HELP:
            if ((int)p2 != 0)
                return TRUE;
            return DisplayHelp(wnd, db->HelpName);
        default:
            break;
    }
    return FALSE;
}

/* ----- window-processing module, DIALOG window class ----- */
int DialogProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    DBOX *db = wnd->extension;

    switch (msg)    {
        case CREATE_WINDOW:
            return CreateWindowMsg(wnd, p1, p2);
        case SETFOCUS:
            if (wnd->Modal)    {
                if (p1)
                    SendMessage(inFocus, SETFOCUS, FALSE, 0);
                inFocus = p1 ? wnd : NULL;
                return TRUE;
            }
            break;
        case SHIFT_CHANGED:
            if (wnd->Modal)
                return TRUE;
            break;
        case LEFT_BUTTON:
            if (LeftButtonMsg(wnd, p1, p2))
                return TRUE;
            break;
        case KEYBOARD:
            if (KeyboardMsg(wnd, p1, p2))
                return TRUE;
            break;
        case CLOSE_POPDOWN:
            SysMenuOpen = FALSE;
            break;
        case LB_SELECTION:
        case LB_CHOOSE:
            if (SysMenuOpen)
                return TRUE;
            SendMessage(wnd, COMMAND, inFocusCommand(db), msg);
            break;
        case COMMAND:
            if (CommandMsg(wnd, p1, p2))
                return TRUE;
            break;
        case PAINT:
            p2 = TRUE;
            break;
        case CLOSE_WINDOW:
            if (!p1)    {
                SendMessage(wnd, COMMAND, ID_CANCEL, 0);
                return TRUE;
            }
            break;
        default:
            break;
    }
    return BaseWndProc(DIALOG, wnd, msg, p1, p2);
}

/* ------- create and execute a dialog box ---------- */
BOOL DialogBox(WINDOW wnd, DBOX *db, BOOL Modal,
  int (*wndproc)(struct window *, enum messages, PARAM, PARAM))
{
    BOOL rtn;
    int x = db->dwnd.x, y = db->dwnd.y;
    CTLWINDOW *ct;
    WINDOW oldFocus = inFocus;
    WINDOW DialogWnd;

    if (!Modal && wnd != NULL)    {
        x += GetLeft(wnd);
        y += GetTop(wnd);
    }
    DialogWnd = CreateWindow(DIALOG,
                        db->dwnd.title,
                        x, y,
                        db->dwnd.h,
                        db->dwnd.w,
                        db,
                        wnd,
                        wndproc,
                        Modal ? SAVESELF : 0);
    DialogWnd->Modal = Modal;
    SendMessage(inFocus, SETFOCUS, FALSE, 0);
    SendMessage(DialogWnd, SHOW_WINDOW, 0, 0);
    SendMessage(((CTLWINDOW *)(DialogWnd->dFocus))->wnd,
        SETFOCUS, TRUE, 0);
    SendMessage(DialogWnd, INITIATE_DIALOG, 0, 0);
    if (Modal)    {
        SendMessage(DialogWnd, CAPTURE_MOUSE, 0, 0);
        SendMessage(DialogWnd, CAPTURE_KEYBOARD, 0, 0);
        while (dispatch_message())
            ;
        rtn = DialogWnd->ReturnCode == ID_OK;
        SendMessage(DialogWnd, RELEASE_MOUSE, 0, 0);
        SendMessage(DialogWnd, RELEASE_KEYBOARD, 0, 0);
        SendMessage(inFocus, SETFOCUS, FALSE, 0);
        SendMessage(DialogWnd, CLOSE_WINDOW, TRUE, 0);
        SendMessage(oldFocus, SETFOCUS, TRUE, 0);
        if (rtn)    {
            ct = db->ctl;
            while (ct->class)    {
                ct->wnd = NULL;
                if (ct->class == RADIOBUTTON ||
                        ct->class == CHECKBOX)
                    ct->isetting = ct->setting;
                ct++;
            }
        }
        return rtn;
    }
    return FALSE;
}

/* ----- return command code of in-focus control window ---- */
static int inFocusCommand(DBOX *db)
{
    CTLWINDOW *ct = db->ctl;
    while (ct->class)    {
        if (ct->wnd == inFocus)
            return ct->command;
        ct++;
    }
    return -1;
}

/* -------- find a specified control structure ------- */
CTLWINDOW *FindCommand(DBOX *db, enum commands cmd, int class)
{
    CTLWINDOW *ct = db->ctl;
    while (ct->class)    {
        if (ct->class == class)
            if (cmd == ct->command)
                return ct;
        ct++;
    }
    return NULL;
}

/* ---- return the window handle of a specified command ---- */
WINDOW ControlWindow(DBOX *db, enum commands cmd)
{
    CTLWINDOW *ct = db->ctl;
    while (ct->class)    {
        if (ct->class != TEXT && cmd == ct->command)
            return ct->wnd;
        ct++;
    }
    return NULL;
}

/* ---- set a control ON or OFF ----- */
void ControlSetting(DBOX *db, enum commands cmd,
                                int class, int setting)
{
    CTLWINDOW *ct = FindCommand(db, cmd, class);
    if (ct != NULL)
        ct->isetting = setting;
}

/* ---- return pointer to the text of a control window ---- */
char *GetDlgTextString(DBOX *db,enum commands cmd,CLASS class)
{
    CTLWINDOW *ct = FindCommand(db, cmd, class);
    if (ct != NULL)
        return ct->itext;
    else
        return NULL;
}

/* ------- set the text of a control specification ------ */
void SetDlgTextString(DBOX *db, enum commands cmd,
                                    char *text, CLASS class)
{
    CTLWINDOW *ct = FindCommand(db, cmd, class);
    if (ct != NULL)    {
        ct->itext = realloc(ct->itext, strlen(text)+1);
        if (ct->itext != NULL)
            strcpy(ct->itext, text);
    }
}

/* ------- set the text of a control window ------ */
void PutItemText(WINDOW wnd, enum commands cmd, char *text)
{
    CTLWINDOW *ct = FindCommand(wnd->extension, cmd, EDITBOX);

    if (ct == NULL)
        ct = FindCommand(wnd->extension, cmd, TEXTBOX);
    if (ct == NULL)
        ct = FindCommand(wnd->extension, cmd, COMBOBOX);
    if (ct == NULL)
        ct = FindCommand(wnd->extension, cmd, LISTBOX);
    if (ct == NULL)
        ct = FindCommand(wnd->extension, cmd, SPINBUTTON);
    if (ct == NULL)
        ct = FindCommand(wnd->extension, cmd, TEXT);
    if (ct != NULL)        {
        WINDOW cwnd = (WINDOW) (ct->wnd);
        switch (ct->class)    {
            case COMBOBOX:
            case EDITBOX:
                SendMessage(cwnd, CLEARTEXT, 0, 0);
                SendMessage(cwnd, ADDTEXT, (PARAM) text, 0);
                if (!isMultiLine(cwnd))
                    SendMessage(cwnd, PAINT, 0, 0);
                break;
            case LISTBOX:
            case TEXTBOX:
            case SPINBUTTON:
                SendMessage(cwnd, ADDTEXT, (PARAM) text, 0);
                break;
            case TEXT:    {
                SendMessage(cwnd, CLEARTEXT, 0, 0);
                SendMessage(cwnd, ADDTEXT, (PARAM) text, 0);
                SendMessage(cwnd, PAINT, 0, 0);
                break;
            }
            default:
                break;
        }
    }
}

/* ------- get the text of a control window ------ */
void GetItemText(WINDOW wnd, enum commands cmd,
                                char *text, int len)
{
    CTLWINDOW *ct = FindCommand(wnd->extension, cmd, EDITBOX);
    unsigned char *cp;

    if (ct == NULL)
        ct = FindCommand(wnd->extension, cmd, COMBOBOX);
    if (ct == NULL)
        ct = FindCommand(wnd->extension, cmd, TEXTBOX);
    if (ct == NULL)
        ct = FindCommand(wnd->extension, cmd, TEXT);
    if (ct != NULL)    {
        WINDOW cwnd = (WINDOW) (ct->wnd);
        if (cwnd != NULL)    {
            switch (ct->class)    {
                case TEXT:
                    if (GetText(cwnd) != NULL)    {
                        cp = strchr(GetText(cwnd), '\n');
                        if (cp != NULL)
                            len = (int) (cp - GetText(cwnd));
                        strncpy(text, GetText(cwnd), len);
                        *(text+len) = '\0';
                    }
                    break;
                case TEXTBOX:
                    if (GetText(cwnd) != NULL)
                        strncpy(text, GetText(cwnd), len);
                    break;
                case COMBOBOX:
                case EDITBOX:
                    SendMessage(cwnd,GETTEXT,(PARAM)text,len);
                    break;
                default:
                    break;
            }
        }
    }
}

/* ------- set the text of a listbox control window ------ */
void GetDlgListText(WINDOW wnd, char *text, enum commands cmd)
{
    CTLWINDOW *ct = FindCommand(wnd->extension, cmd, LISTBOX);
    int sel = SendMessage(ct->wnd, LB_CURRENTSELECTION, 0, 0);
    SendMessage(ct->wnd, LB_GETTEXT, (PARAM) text, sel);
}

/* -- find control structure associated with text control -- */
static CTLWINDOW *AssociatedControl(DBOX *db,enum commands Tcmd)
{
    CTLWINDOW *ct = db->ctl;
    while (ct->class)    {
        if (ct->class != TEXT)
            if (ct->command == Tcmd)
                break;
        ct++;
    }
    return ct;
}

/* --- process dialog box shortcut keys --- */
static void dbShortcutKeys(DBOX *db, int ky)
{
    CTLWINDOW *ct;
    int ch = AltConvert(ky);

    if (ch != 0)    {
        ct = db->ctl;
        while (ct->class)    {
            char *cp = ct->itext;
            while (cp && *cp)    {
                if (*cp == SHORTCUTCHAR &&
                            tolower(*(cp+1)) == ch)    {
                    if (ct->class == TEXT)
                        ct = AssociatedControl(db, ct->command);
                    if (ct->class == RADIOBUTTON)
                        SetRadioButton(db, ct);
                    else if (ct->class == CHECKBOX)    {
                        ct->setting ^= ON;
                        SendMessage(ct->wnd, PAINT, 0, 0);
                    }
                    else if (ct->class)    {
                        SendMessage(ct->wnd, SETFOCUS, TRUE, 0);
                        if (ct->class == BUTTON)
                           SendMessage(ct->wnd,KEYBOARD,'\r',0);
                    }
                    return;
                }
                cp++;
            }
            ct++;
        }
    }
}

/* --- dynamically add or remove scroll bars
                            from a control window ---- */
void SetScrollBars(WINDOW wnd)
{
    int oldattr = GetAttribute(wnd);
    if (wnd->wlines > ClientHeight(wnd))
        AddAttribute(wnd, VSCROLLBAR);
    else
        ClearAttribute(wnd, VSCROLLBAR);
    if (wnd->textwidth > ClientWidth(wnd))
        AddAttribute(wnd, HSCROLLBAR);
    else
        ClearAttribute(wnd, HSCROLLBAR);
    if (GetAttribute(wnd) != oldattr)
        SendMessage(wnd, BORDER, 0, 0);
}

/* ------- CREATE_WINDOW Message (Control) ----- */
static void CtlCreateWindowMsg(WINDOW wnd)
{
    CTLWINDOW *ct;
    ct = wnd->ct = wnd->extension;
    wnd->extension = NULL;
    if (ct != NULL)
        ct->wnd = wnd;
}

/* ------- KEYBOARD Message (Control) ----- */
static BOOL CtlKeyboardMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    CTLWINDOW *ct = GetControl(wnd);
    switch ((int) p1)    {
        case F1:
            if (WindowMoving || WindowSizing)
                break;
            if (!DisplayHelp(wnd, ct->help))
                SendMessage(GetParent(wnd),COMMAND,ID_HELP,0);
            return TRUE;
        case ' ':
            if (!((int)p2 & ALTKEY))
                break;
        case ALT_F6:
        case CTRL_F4:
        case ALT_F4:
            PostMessage(GetParent(wnd), KEYBOARD, p1, p2);
            return TRUE;
        default:
            break;
    }
    if (GetClass(wnd) == EDITBOX)
        if (isMultiLine(wnd))
            return FALSE;
    switch ((int) p1)    {
        case UP:
            if (!isDerivedFrom(wnd, LISTBOX))    {
                p1 = CTRL_FIVE;
                p2 = LEFTSHIFT;
            }
            break;
        case BS:
            if (!isDerivedFrom(wnd, EDITBOX))    {
                p1 = CTRL_FIVE;
                p2 = LEFTSHIFT;
            }
            break;
        case DN:
            if (!isDerivedFrom(wnd, LISTBOX) &&
                    !isDerivedFrom(wnd, COMBOBOX))
                p1 = '\t';
            break;
        case FWD:
            if (!isDerivedFrom(wnd, EDITBOX))
                p1 = '\t';
            break;
        case '\r':
            if (isDerivedFrom(wnd, EDITBOX))
                if (isMultiLine(wnd))
                    break;
            if (isDerivedFrom(wnd, BUTTON))
                break;
            SendMessage(GetParent(wnd), COMMAND, ID_OK, 0);
            return TRUE;
        default:
            break;
    }
    return FALSE;
}

/* ------- CLOSE_WINDOW Message (Control) ----- */
static void CtlCloseWindowMsg(WINDOW wnd)
{
    CTLWINDOW *ct = GetControl(wnd);
    if (ct != NULL)    {
        if (GetParent(wnd)->ReturnCode == ID_OK &&
                (ct->class == EDITBOX ||
                    ct->class == COMBOBOX))    {
            if (wnd->TextChanged)    {
                ct->itext=realloc(ct->itext,strlen(wnd->text)+1);
                strcpy(ct->itext, wnd->text);
                if (!isMultiLine(wnd))    {
                    char *cp = ct->itext+strlen(ct->itext)-1;
                    if (*cp == '\n')
                        *cp = '\0';
                }
            }
        }
    }
}

/* -- generic window processor used by dialog box controls -- */
static int ControlProc(WINDOW wnd,MESSAGE msg,PARAM p1,PARAM p2)
{
    DBOX *db;
    CTLWINDOW *ct;

    if (wnd == NULL)
        return FALSE;
    db = GetParent(wnd) ? GetParent(wnd)->extension : NULL;
    ct = GetControl(wnd);

    switch (msg)    {
        case CREATE_WINDOW:
            CtlCreateWindowMsg(wnd);
            break;
        case KEYBOARD:
            if (CtlKeyboardMsg(wnd, p1, p2))
                return TRUE;
            break;
        case PAINT:
            if (GetClass(wnd) == EDITBOX ||
                    GetClass(wnd) == LISTBOX ||
                        GetClass(wnd) == TEXTBOX)
                SetScrollBars(wnd);
            break;
        case BORDER:
            if (GetClass(wnd) == EDITBOX)    {
                WINDOW oldFocus = inFocus;
                inFocus = NULL;
                DefaultWndProc(wnd, msg, p1, p2);
                inFocus = oldFocus;
                return TRUE;
            }
            break;
        case SETFOCUS:
            if (p1)    {
                DefaultWndProc(wnd, msg, p1, p2);
                GetParent(wnd)->dFocus = ct;
                SendMessage(GetParent(wnd), COMMAND,
                    inFocusCommand(db), ENTERFOCUS);
                return TRUE;
            }
            else
                SendMessage(GetParent(wnd), COMMAND,
                    inFocusCommand(db), LEAVEFOCUS);
            break;
        case CLOSE_WINDOW:
            CtlCloseWindowMsg(wnd);
            break;
        default:
            break;
    }
    return DefaultWndProc(wnd, msg, p1, p2);
}

/* ---- change the focus to the next or previous control --- */
static void ChangeFocus(WINDOW wnd, int direc)
{
    DBOX *db = wnd->extension;
     CTLWINDOW *ct = db->ctl;
     CTLWINDOW *ctt;

    /* --- find the control that has the focus --- */
    while (ct->class)    {
        if (ct == wnd->dFocus)
            break;
        ct++;
    }
    if (ct->class)    {
        ctt = ct;
        do    {
            /* ----- point to next or previous control ----- */
            if (direc)    {
                ct++;
                if (ct->class == 0)
                    ct = db->ctl;
            }
            else    {
                if (ct == db->ctl)
                    while (ct->class)
                        ct++;
                --ct;
            }

            if (ct->class != BOX && ct->class != TEXT)    {
                SendMessage(ct->wnd, SETFOCUS, TRUE, 0);
                SendMessage(ctt->wnd, PAINT, 0, 0);
                SendMessage(ct->wnd, PAINT, 0, 0);
                break;
            }
        } while (ct != ctt);
    }
}

void SetFocusCursor(WINDOW wnd)
{
    if (wnd == inFocus)    {
        SendMessage(NULL, SHOW_CURSOR, 0, 0);
        SendMessage(wnd, KEYBOARD_CURSOR, 1, 0);
    }
}


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.