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


APR92: C PROGRAMMING

This article contains the following executables: DFLT11.ARC

This month we add menus to D-Flat. Applications use menus to present command selections to the user. The menu lists commands from which the user may choose. A command tells the program to do something, and that's how the users exercise the functions and features of an application.

We are nearing the end of this series. The D-Flat project is almost a year old, and it will run another four months. If you started at the beginning, you have absorbed a lot of knowledge. As a side effect, you have learned about event-driven, message-based programming, a good lesson because that is how much of tomorrow's programming will be done -- perhaps wrapped in an object-oriented shroud as well. There is a lot more to know about programming than there used to be. In fact...

The Myth of the Obsolete Programmer

Programming ain't gettin' any easier. That's not how it was supposed to be, however. Not long ago, we were hearing that 4GLs were going to make us programmers obsolete. About ten years ago James Martin, the self-anointed systems guru, wrote a ridiculous book called Applications Development Without Programming. I started reading the book with some dismay, I must tell you. I had respect for Martin's earlier works on database technology, and here he was predicting that programmers would be unnecessary because users would be writing their own programs. The anxiety was premature, however. My fears for the future were allayed when I got to the part where Martin suggested some contemporary languages with which users would soon obviate programmers. One of them was APL. I stopped reading and gave the book to Fast Eddie Dwyer, who used it for a door stop.

Now, a decade later, we are no closer to user-written programs than we were when James Martin made his silly predictions. Why? Because to be a complete programmer you still need to know how to design, code, test, and install a program, and for that you need skill, discipline, and structure, and all that is getting harder, not easier.

To begin with, programming takes more computer hardware than it used to. I have several of the latest C++ compilers for the PC. Guess what? They do not work without a lot of hardware. For example, some of them need 2 or more Mbytes of extended memory. Some run under Windows, which itself needs plenty of gear. You cannot install some of the compilers without at least 40 spare Mbytes of hard disk. Most of the new breed of compilers are much bigger than their forebears -- bigger even than the hard disk of one of my computers. They look like Weird Al Yankovic in his video. Why so big? New features, mostly. It takes new features for a compiler to continue to tap the sap of the follow-on market. You don't sell upgrades without new features. New platforms, too. Compilers need to support Windows, extended DOS, and protected mode. And then there is the new C++ paradigm, which is bigger than C.

All that feature-laden size adds language, functions, types, and so on, which turn into high-octane complexity. The ways of doing things change so rapidly that you struggle to find the reusable parts of a program. The user interface in a typical program takes up most of its code now. I've listened to programmers talk about porting Windows programs to the Mac and back. They can't ever find the application buried in all that incompatible user interface/file manager/database engine/ environment-dependent code. They wind up rewriting the program.

User-written programs, indeed. Just imagine your doctor, accountant, and car salesperson each installing DOS, Windows, a C++ compiler, function and class libraries, a resource editor, debuggers, profilers, a database engine, a CASE tool, and the very proper, compatible, extended and expanded memory managers. Now imagine them dutifully and patiently plugging away, encapsulating, instantiating, polymorphizing, and getting their applications designed, written, tested, and up and running -- during which time the patients croak, the clients go broke, and the cars all rust on the lot.

No, programming ain't gettin' any easier, and that's good. They'll be needing us for a spell to come, I'll hazard a guess.

D-Flat Menus

D-Flat implements the Common User Access (CUA) specification, which identifies standard menus, standard menu commands, and the effect and behavior of each. The specification further provides for custom menus and commands that are unique to the application. The CUA menu system starts with a menu bar that extends across the top of the application window, directly under the title bar and above the data area. The menu bar is one character line high, and it has labels that identify the application's menus. When you select a menu label from the menu bar, a pop-down menu pops down under the label. Then you select a command from the pop-down menu.

In September of last year, this column showed how a programmer defines the menu bar and its pop-down menus by coding a file named menu.c. This file contains macro calls the C compiler's preprocessor translates into structure declarations and initializations by using the macros defined in menu.h, which I discussed in September, too. This month, I show you the code from the D-Flat library that implements the menus.

The Menu Bar

Listing One, page 150, is menubar.c, the code that implements the menu bar. When you create the application window, you specify the name of the menu you described in menu.c. The code for the APPLICATION window class creates a window of the MENUBAR class. The MENUBAR window uses the named menu structures to define its contents. You will learn about the APPLICATION window class next month. The menu bar becomes a part of the application window and displays when the application window displays.

The program in Listing One consists of a window-processing module for the MENUBAR class and some functions that process each of the window's messages. The CREATE_WINDOW message allocates memory for the menu bar and sets that memory to spaces. The SETFOCUS message sets the menu bar's active selection to the first selection if one is not selected. It paints the window and tells its parent to clear the status bar if the menu bar is giving up the focus.

The APPLICATION window sends the BUILDMENU message to the menu bar to tell it to add pop-down menus to itself. The parameter in the BUILDMENU message is the address of the menu structure defined in menu.c. The message begins by building a blank menu bar. Then it builds a table of menu selections with an entry for each of the pop-down menus defined in the structure. The table records the beginning and ending x-coordinate positions of the menu label in the menu bar and the character value of the menu selection's shortcut key. That value is defined in the structure's text with a tilde (~) ahead of the shortcut-key character. The character displays in a highlight color on the menu bar. Each menu-bar selection causes the length of the menu-bar's display string to grow to accommodate the control strings that change and reset the colors around the shortcut key. The CopyCommand function copies the menu selection's name into the menu-bar display string, inserting the color controls where it finds the tilde.

The PAINT message calls the wputs function to display the menu bar. Then, if the menu bar has the focus or if a pop-down menu is being displayed, the message paints the label of the current active menu-bar selection in the reverse colors of the menu bar. If the menu bar has the focus and no pop-down menu is being displayed, the message displays the menu-bar selection's help text in the application window's status bar by sending the ADDSTATUS message.

When the user presses a key, the D-Flat message system captures the keyboard event, turns it into a KEYBOARD message, and sends the message to the window that has the focus. If the window processes the keystroke, it returns. If not, the window passes the message up the window class hierarchy. Each class in the hierarchy to which the window belongs gets a crack at the KEYBOARD message. Remember that this is all in the name of the in-focus window. At the top of the hierarchy is the NORMAL window class. If the NORMAL class does not process the keystroke, this particular window is not interested in the keystroke, and the NORMAL window-processing module sends the KEYBOARD message to the parent window of the window that has the focus. The pass through the hierarchy repeats, this time tracing the class hierarchy of the parent window class. The message passes this way through the classes of each parent and then through the generations from parent to parent until either one of the windows processes the message or the applications window receives the message in the window-processing module of the APPLICATION class. If the application window does not process the message, the window passes the message to the application's menu- bar window. That's how the menu bar eventually gets keystrokes that no other window wants. The message makes a long trek on a busy screen. Why, the whole thing could take microseconds.

KEYBOARD messages that make it all the way to the menu bar might be shortcut keys for the menu-bar labels or accelerator keys for the pop-down menus themselves. If the value of the keystroke matches one of the menu-bar shortcut keys and the Alt key is down, the program sends the MB_SELECTION message to the menu-bar window, telling the window to pop down the selected pop-down menu just as if the user had clicked it with the mouse. If the value of the keystroke matches one of the accelerator keys on a pop-down window, the program does what the POP-DOWNMENU window does when the user chooses the corresponding selection from the pop-down menu. The program inverts the toggle setting of a TOGGLE menu selection, returns the focus to the most recent document window that had the focus, and sends the COMMAND message to the application window along with the command code that matches the menu selection.

The KEYBOARD message for the menu-bar processes some other key values. The F1 key is the help key, and each menu label has an associated help screen. If no pop-down window is open and the user presses F1, the program calls the DisplayHelp function with the active menu-bar label as the help parameter. If the menu bar has the focus but no pop-down menu is open, the Enter key means that the user wants to pop down the menu of the selected menu-bar label. The program sends the appropriate MB_SELECTION message to the menu bar. The user can use the F10 key to toggle the focus between the menu bar and a document window, and the Esc key to move the focus from the menu bar to the most recent document window. The right- and left-arrow keys, FWD and BS, change the selected menu label on the menu bar. If a pop-down menu is open, these keys send the MB_SELECTION message as well to open a different pop-down menu.

When the user clicks the left button on a menu-bar label, the menu-bar window translates the LEFT_BUTTON message coordinates into the chosen label and sends the MB_SELECTION message to the menu bar.

The MB_SELECTION message tells the menu bar that the user has chosen a pop-down menu. If the pop-down menu has a preparation function defined, the program calls that function first. If the second parameter of the MB_SELECTION is true, the chosen menu is a cascaded menu, one that cascades down from the selection of another menu. For example, the Tabs selection on the Options menu of the example memopad program calls a cascaded menu of tab settings. The program computes the screen location of a cascaded menu as a function of the location of the pop-down menu from which it cascades. The position of menus that do not cascade is computed from the position of the selection's label on the menu bar.

If a pop-down menu is already open when the MB_SELECTION message arrives, and the new menu is not being cascaded, the program closes the current one. Next, it creates the new pop-down menu window. If the pop-down menu has no selection text, then the program does not display the menu. The Window menu will have no text if there are no document windows open, for example. If there are selections, however, the program sends the BUILD_SELECTIONS and SETFOCUS messages to the pop-down menu.

When the user chooses a selection from a menu, the POPDOWNMENU window sends the COMMAND message to the menu bar with the associated command identification code as a parameter. If the command points to a cascaded menu, the program sends the menu bar the MB_COMMAND message with the menu code for a parameter and a true value for the second parameter. If the chosen command does not point to a cascaded menu, the program closes the pop-down menu window, sends the SETFOCUS message to the document window that had the focus before the user selected the pop-down menu, and posts the COMMAND message to the application window.

When the POPDOWNMENU window closes, it sends the CLOSE_POPDOWN message to its parent, in this case the menu bar. If the pop-down menu is a cascaded menu, the menu bar sends its parent a CLOSE_WINDOW message. Otherwise, the menu bar cleans house by setting the active selection to a null value, returning the focus to the most recent in-focus document window, and repainting itself to turn off the highlight on the selection label. When the menu bar closes, it frees the memory allocated for its display and sets its pointers to null values.

The Pop-down Menu

Listing Two, page 152, is popdown.c, the code that implements the POPDOWN-MENU window class. A pop-down menu window derives from the LISTBOX class. The POPDOWNMENU class adds some unique message processing to give the menu list box the appearance and behavior of a pop-down menu. The CREATE_WINDOW message captures the mouse and keyboard into the window. When a pop-down window is opened, no other window can take over until the pop-down window turns things loose.

A list-box window uses a single click to position the mouse cursor on the selection and a double click to choose the selection for further action. A pop-down menu ignores double clicks and uses the single click to make the choice. The program sends the LB_SELECTION message to position the selection cursor bar when the pop-down menu window gets the LEFT_BUTTON message. Then, when the window gets the BUTTON_RELEASED message, the program sends the LB_CHOOSE message to indicate the user's choice.

Each time a pop-down menu window gets a PAINT message, it rebuilds the text-display buffer. This is because the display characteristics of the selections change, depending on whether the selections are active or not. An inactive selection displays with characters of lesser intensity. The BORDER message temporarily disables the in-Focus variable so that D-Flat will display a single-line border, the usual border for windows that do not have the focus. Then it changes the vertical border characters for those lines on the menu that have separator bars between the selections.

The LB_SELECTION message rejects itself if the user selects a separator bar on the menu. Otherwise, it allows the base LISTBOX class to process the message.

The LB_CHOOSE message means that the user has chosen a menu selection to execute. If the command is currently inactive, the program calls the beep function to sound a warning. Otherwise, the program inverts the command's check-mark display if the command is a toggle, and then sends the COMMAND message with the command's identification code to the pop-down menu's parent window (in most cases, the menu bar).

A pop-down menu selection may have a shortcut key and an accelerator key. The KEYBOARD message tests the keystroke to see if it is one of these. If so, the program sends the LB_SELECTION and LB_CHOOSE messages to the pop-down menu window.

The KEYBOARD message can do one of several other things, depending on the keystroke. The up- and down-arrow keys work like other list boxes, except that the pop-down menu assumes that the entire display fits in the window and does not scroll. Instead, the cursor wraps from bottom to top and top to bottom. The right- and left-arrow keys mean that the user wants to close the current pop-down menu and open the one to the right or the left. Because the menu-bar window handles that, the pop-down window passes those keys to the menu bar. The F1 help key calls the Display-Help function, passing the help identification of the current menu selection. The Esc key closes the pop-down window.

The CLOSE_WINDOW message releases the mouse and the keyboard and sends the CLOSE_POPDOWN message to the pop-down menu window's parent.

The Menu API

Listing Three, page 153, is menu.c, which contains functions an applications program can use to inquire about and change certain characteristics of the D-Flat menus and their commands. The FindCmd function is local to the source file. The other functions use it to get a pointer to the PopDown structure that supports a specified command identification.

The isActive function returns true if the command on the menu is active. The GetCommandText function returns the address of a menu command's title text. The ActivateCommand and DeactivateCommand functions activate and deactivate a command on a menu.

Some menu commands are toggles rather than process executors. Examples are the Insert and Word wrap commands on the Options menu. The GetCommandToggle, SetCommandToggle, ClearCommandToggle, and InvertCommandToggle functions get, set, clear, and invert the toggle setting for a specified command on a specified menu.

The System Menu

The CUA specification defines the system menu for all windows. It is a menu the user calls by selecting the control box in the upper-left corner of the window. It includes commands to move, size, restore, minimize, and maximize the window, and its purpose is to allow a user without a mouse to perform those operations with the keyboard.

Listing Four, page 154, is sysmenu.c, the code that implements the standard system menus that associate with most other window types. D-Flat calls BuildSystemMenu when the user presses the Alt+minus or Alt+spacebar keys or clicks a window's control box. The function computes the system menu's position from the location of the parent window's control box and creates the system-menu window. Then it uses the ActivateCommand and DeactivateCommand functions to activate and deactivate the system-menu commands based on the condition of the parent window. You can't restore a window that is not minimized or maximized, for example. Once the commands are prepared, the program sends the BUILD_SELECTIONS and SHOW_WINDOW messages to the menu window to get it built, displayed, and in control.

The SystemMenuProc function is the window-processing module for the system menu. The CREATE_WINDOW message changes the active menu bar to the dummy one associated with the system menu. The LEFT_BUTTON message gets intercepted when it hits the control button of the parent window. The LB_CHOOSE message closes the window. If the DOUBLE_CLICK message hits the control box of the parent, that is the same as closing the window, so the program posts the DOUBLE_ CLICK message to the parent so it will close itself, and then closes the system-menu window.

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 care to, stuff a dollar bill in the mailer for the Brevard County Food Bank. They help homeless and hungry families here in my home town. We've collected over $1000 so far from generous D-Flat "careware" users. If you want to discuss D-Flat with me, use CompuServe. My CompuServe ID is 71101,1262, and I monitor the DDJ Forum daily.

Next month we'll discuss the D-Flat APPLICATION window class.



_C PROGRAMMING COLUMN_
by Al Stevens


[LISTING ONE]
<a name="00f9_000c">


/* ---------------- menubar.c ------------------ */

#include "dflat.h"

static void reset_menubar(WINDOW);

static struct {
    int x1, x2;     /* position in menu bar */
    char sc;        /* shortcut key value   */
} menu[10];
static int mctr;

MBAR *ActiveMenuBar;
static MENU *ActiveMenu;

static WINDOW mwnd;
static BOOL Selecting;

static WINDOW Cascaders[MAXCASCADES];
static int casc;
static WINDOW GetDocFocus(WINDOW);

/* ----------- SETFOCUS Message ----------- */
static void SetFocusMsg(WINDOW wnd, PARAM p1)
{
    if ((int)p1 && ActiveMenuBar->ActiveSelection == -1)
        ActiveMenuBar->ActiveSelection = 0;
    SendMessage(wnd, PAINT, 0, 0);
    if (!(int)p1)
        SendMessage(GetParent(wnd), ADDSTATUS, 0, 0);
}

/* --------- BUILDMENU Message --------- */
static void BuildMenuMsg(WINDOW wnd, PARAM p1)
{
    int offset = 3;
    reset_menubar(wnd);
    mctr = 0;
    ActiveMenuBar = (MBAR *) p1;
    ActiveMenu = ActiveMenuBar->PullDown;
    while (ActiveMenu->Title != NULL &&
            ActiveMenu->Title != (void*)-1)    {
        char *cp;
        if (strlen(GetText(wnd)+offset) <
                strlen(ActiveMenu->Title)+3)
            break;
        GetText(wnd) = realloc(GetText(wnd),
            strlen(GetText(wnd))+5);
        memmove(GetText(wnd) + offset+4, GetText(wnd) + offset,
                strlen(GetText(wnd))-offset+1);
        CopyCommand(GetText(wnd)+offset,ActiveMenu->Title,FALSE,
                wnd->WindowColors [STD_COLOR] [BG]);
        menu[mctr].x1 = offset;
        offset += strlen(ActiveMenu->Title) + (3+MSPACE);
        menu[mctr].x2 = offset-MSPACE;
        cp = strchr(ActiveMenu->Title, SHORTCUTCHAR);
        if (cp)
            menu[mctr].sc = tolower(*(cp+1));
        mctr++;
        ActiveMenu++;
    }
    ActiveMenu = ActiveMenuBar->PullDown;
}

/* ---------- PAINT Message ---------- */
static void PaintMsg(WINDOW wnd)
{
    if (wnd == inFocus)
        SendMessage(GetParent(wnd), ADDSTATUS, 0, 0);
    SetStandardColor(wnd);
    wputs(wnd, GetText(wnd), 0, 0);
    if (ActiveMenuBar->ActiveSelection != -1 &&
            (wnd == inFocus || mwnd != NULL))    {
        char *sel;
        char *cp;
        if ((sel = malloc(200)) != NULL)    {
            int offset=menu[ActiveMenuBar->ActiveSelection].x1;
            int offset1=menu[ActiveMenuBar->ActiveSelection].x2;
            GetText(wnd)[offset1] = '\0';
            SetReverseColor(wnd);
            memset(sel, '\0', 200);
            strcpy(sel, GetText(wnd)+offset);
            cp = strchr(sel, CHANGECOLOR);
            if (cp != NULL)
                *(cp + 2) = background | 0x80;
            wputs(wnd, sel,
                offset-ActiveMenuBar->ActiveSelection*4, 0);
            GetText(wnd)[offset1] = ' ';
            if (!Selecting && mwnd == NULL && wnd == inFocus) {
                char *st = ActiveMenu
                    [ActiveMenuBar->ActiveSelection].StatusText;
                if (st != NULL)
                    SendMessage(GetParent(wnd), ADDSTATUS,
                        (PARAM)st, 0);
            }
            free(sel);
        }
    }
}

/* ------------ KEYBOARD Message ------------- */
static void KeyboardMsg(WINDOW wnd, PARAM p1)
{
    MENU *mnu;
    if (mwnd == NULL)    {
        /* ----- search for menu bar shortcut keys ---- */
        int c = tolower((int)p1);
        int a = AltConvert((int)p1);
        int j;
        for (j = 0; j < mctr; j++)    {
            if ((inFocus == wnd && menu[j].sc == c) ||
                    (a && menu[j].sc == a))    {
                SendMessage(wnd, MB_SELECTION, j, 0);
                return;
            }
        }
    }
    /* -------- search for accelerator keys -------- */
    mnu = ActiveMenu;
    while (mnu->Title != (void *)-1)    {
        struct PopDown *pd = mnu->Selections;
        if (mnu->PrepMenu)
            (*(mnu->PrepMenu))(GetDocFocus(wnd), mnu);
        while (pd->SelectionTitle != NULL)    {
            if (pd->Accelerator == (int) p1)    {
                if (pd->Attrib & INACTIVE)
                    beep();
                else    {
                    if (pd->Attrib & TOGGLE)
                        pd->Attrib ^= CHECKED;
                    SendMessage(GetDocFocus(wnd),
                        SETFOCUS, TRUE, 0);
                    PostMessage(GetParent(wnd),
                        COMMAND, pd->ActionId, 0);
                }
                return;
            }
            pd++;
        }
        mnu++;
    }
    switch ((int)p1)    {
        case F1:
            if (ActiveMenu != NULL &&
                (mwnd == NULL ||
                (ActiveMenu+ActiveMenuBar->ActiveSelection)->
                    Selections[0].SelectionTitle == NULL)) {
                DisplayHelp(wnd,
        (ActiveMenu+ActiveMenuBar->ActiveSelection)->Title+1);
                return;
            }
            break;
        case '\r':
            if (mwnd == NULL &&
                    ActiveMenuBar->ActiveSelection != -1)
                SendMessage(wnd, MB_SELECTION,
                    ActiveMenuBar->ActiveSelection, 0);
            break;
        case F10:
            if (wnd != inFocus && mwnd == NULL)    {
                SendMessage(wnd, SETFOCUS, TRUE, 0);
                break;
            }
            /* ------- fall through ------- */
        case ESC:
            if (inFocus == wnd && mwnd == NULL)    {
                ActiveMenuBar->ActiveSelection = -1;
                SendMessage(GetDocFocus(wnd),SETFOCUS,TRUE,0);
                SendMessage(wnd, PAINT, 0, 0);
            }
            break;
        case FWD:
            ActiveMenuBar->ActiveSelection++;
            if (ActiveMenuBar->ActiveSelection == mctr)
                ActiveMenuBar->ActiveSelection = 0;
            if (mwnd != NULL)
                SendMessage(wnd, MB_SELECTION,
                    ActiveMenuBar->ActiveSelection, 0);
            else
                SendMessage(wnd, PAINT, 0, 0);
            break;
        case BS:
            if (ActiveMenuBar->ActiveSelection == 0)
                ActiveMenuBar->ActiveSelection = mctr;
            --ActiveMenuBar->ActiveSelection;
            if (mwnd != NULL)
                SendMessage(wnd, MB_SELECTION,
                    ActiveMenuBar->ActiveSelection, 0);
            else
                SendMessage(wnd, PAINT, 0, 0);
            break;
        default:
            break;
    }
}

/* --------------- LEFT_BUTTON Message ---------- */
static void LeftButtonMsg(WINDOW wnd, PARAM p1)
{
    int i;
    int mx = (int) p1 - GetLeft(wnd);
    /* --- compute the selection that the left button hit --- */
    for (i = 0; i < mctr; i++)
        if (mx >= menu[i].x1-4*i &&
                mx <= menu[i].x2-4*i-5)
            break;
    if (i < mctr)
        if (i != ActiveMenuBar->ActiveSelection || mwnd == NULL)
            SendMessage(wnd, MB_SELECTION, i, 0);
}

/* -------------- MB_SELECTION Message -------------- */
static void SelectionMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    int wd, mx, my;
    MENU *mnu;

    Selecting = TRUE;
    mnu = ActiveMenu+(int)p1;
    if (mnu->PrepMenu != NULL)
        (*(mnu->PrepMenu))(GetDocFocus(wnd), mnu);
    wd = MenuWidth(mnu->Selections);
    if (p2)    {
        mx = GetLeft(inFocus) + WindowWidth(inFocus) - 1;
        my = GetTop(inFocus) + inFocus->selection;
    }
    else    {
        int offset = menu[(int)p1].x1 - 4 * (int)p1;
        if (mwnd != NULL)    {
            SendMessage(wnd, SETFOCUS, TRUE, 0);
            SendMessage(mwnd, CLOSE_WINDOW, 0, 0);
        }
        ActiveMenuBar->ActiveSelection = (int) p1;
        if (offset > WindowWidth(wnd)-wd)
            offset = WindowWidth(wnd)-wd;
        mx = GetLeft(wnd)+offset;
        my = GetTop(wnd)+1;
    }
    mwnd = CreateWindow(POPDOWNMENU, NULL,
                mx, my,
                MenuHeight(mnu->Selections),
                wd,
                NULL,
                wnd,
                NULL,
                0);
    AddAttribute(mwnd, SHADOW);
    if (mnu->Selections[0].SelectionTitle != NULL)    {
        SendMessage(mwnd, BUILD_SELECTIONS, (PARAM) mnu, 0);
        SendMessage(mwnd, SETFOCUS, TRUE, 0);
    }
    else
        SendMessage(wnd, PAINT, 0, 0);
    Selecting = FALSE;
}

/* --------- COMMAND Message ---------- */
static void CommandMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    if (isCascadedCommand(ActiveMenuBar, (int)p1))    {
        /* find the cascaded menu based on command id in p1 */
        MENU *mnu = ActiveMenu+mctr;
        while (mnu->Title != (void *)-1)    {
            if (mnu->CascadeId == (int) p1)    {
                if (casc < MAXCASCADES)    {
                    Cascaders[casc++] = mwnd;
                    SendMessage(wnd, MB_SELECTION,
                        (PARAM)(mnu-ActiveMenu), TRUE);
                }
                break;
            }
            mnu++;
        }
    }
    else     {
        if (mwnd != NULL)
            SendMessage(mwnd, CLOSE_WINDOW, 0, 0);
        SendMessage(GetDocFocus(wnd), SETFOCUS, TRUE, 0);
        PostMessage(GetParent(wnd), COMMAND, p1, p2);
    }
}

/* --------------- CLOSE_POPDOWN Message --------------- */
static void ClosePopdownMsg(WINDOW wnd)
{
    if (casc > 0)
        SendMessage(Cascaders[--casc], CLOSE_WINDOW, 0, 0);
    else     {
        mwnd = NULL;
        ActiveMenuBar->ActiveSelection = -1;
        if (!Selecting)
            SendMessage(GetDocFocus(wnd), SETFOCUS, TRUE, 0);
        SendMessage(wnd, PAINT, 0, 0);
    }
}

/* ---------------- CLOSE_WINDOW Message --------------- */
static void CloseWindowMsg(WINDOW wnd)
{
    if (GetText(wnd) != NULL)    {
        free(GetText(wnd));
        GetText(wnd) = NULL;
    }
    mctr = 0;
    ActiveMenuBar->ActiveSelection = -1;
    ActiveMenu = NULL;
    ActiveMenuBar = NULL;
}

/* --- Window processing module for MENUBAR window class --- */
int MenuBarProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int rtn;

    switch (msg)    {
        case CREATE_WINDOW:
            reset_menubar(wnd);
            break;
        case SETFOCUS:
            rtn = BaseWndProc(MENUBAR, wnd, msg, p1, p2);
            SetFocusMsg(wnd, p1);
            return rtn;
        case BUILDMENU:
            BuildMenuMsg(wnd, p1);
            break;
        case PAINT:
            if (!isVisible(wnd) || GetText(wnd) == NULL)
                break;
            PaintMsg(wnd);
            return FALSE;
        case BORDER:
            return TRUE;
        case KEYBOARD:
            KeyboardMsg(wnd, p1);
            return TRUE;
        case LEFT_BUTTON:
            LeftButtonMsg(wnd, p1);
            return TRUE;
        case MB_SELECTION:
            SelectionMsg(wnd, p1, p2);
            break;
        case COMMAND:
            CommandMsg(wnd, p1, p2);
            return TRUE;
        case INSIDE_WINDOW:
            return InsideRect(p1, p2, WindowRect(wnd));
        case CLOSE_POPDOWN:
            ClosePopdownMsg(wnd);
            return TRUE;
        case CLOSE_WINDOW:
            rtn = BaseWndProc(MENUBAR, wnd, msg, p1, p2);
            CloseWindowMsg(wnd);
            return rtn;
        default:
            break;
    }
    return BaseWndProc(MENUBAR, wnd, msg, p1, p2);
}

/* ----- return the WINDOW handle of the document window
     that had the focus when the MENUBAR was activated ----- */
static WINDOW GetDocFocus(WINDOW wnd)
{
    WINDOW DocFocus = Focus.LastWindow;
    CLASS cl;
    while ((cl = GetClass(DocFocus)) == MENUBAR ||
                cl == POPDOWNMENU ||
                    cl == STATUSBAR ||
                        cl == APPLICATION)                    {
        if ((DocFocus = PrevWindow(DocFocus)) == NULL)    {
            DocFocus = GetParent(wnd);
            break;
        }
    }
    return DocFocus;
}

/* ------------- reset the MENUBAR -------------- */
static void reset_menubar(WINDOW wnd)
{
    if ((GetText(wnd) =
            realloc(GetText(wnd), SCREENWIDTH+5)) != NULL)    {
        memset(GetText(wnd), ' ', SCREENWIDTH);
        *(GetText(wnd)+WindowWidth(wnd)) = '\0';
    }
}






<a name="00f9_000d">
<a name="00f9_000e">
[LISTING TWO]
<a name="00f9_000e">

/* ------------- popdown.c ----------- */

#include "dflat.h"

static int SelectionWidth(struct PopDown *);
static int py = -1;

/* ------------ CREATE_WINDOW Message ------------- */
static int CreateWindowMsg(WINDOW wnd)
{
    int rtn;
    ClearAttribute(wnd, HASTITLEBAR     |
                        VSCROLLBAR     |
                        MOVEABLE     |
                        SIZEABLE     |
                        HSCROLLBAR);
    rtn = BaseWndProc(POPDOWNMENU, wnd, CREATE_WINDOW, 0, 0);
    SendMessage(wnd, CAPTURE_MOUSE, 0, 0);
    SendMessage(wnd, CAPTURE_KEYBOARD, 0, 0);
    SendMessage(NULL, SAVE_CURSOR, 0, 0);
    SendMessage(NULL, HIDE_CURSOR, 0, 0);
    return rtn;
}

/* --------- LEFT_BUTTON Message --------- */
static void LeftButtonMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    int my = (int) p2 - GetTop(wnd);
    if (InsideRect(p1, p2, ClientRect(wnd)))    {
        if (my != py)    {
            SendMessage(wnd, LB_SELECTION,
                    (PARAM) wnd->wtop+my-1, TRUE);
            py = my;
        }
    }
    else if ((int)p2 == GetTop(GetParent(wnd)))
        if (GetClass(GetParent(wnd)) == MENUBAR)
            PostMessage(GetParent(wnd), LEFT_BUTTON, p1, p2);
}

/* -------- BUTTON_RELEASED Message -------- */
static BOOL ButtonReleasedMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    py = -1;
    if (InsideRect((int)p1, (int)p2, ClientRect(wnd)))    {
        int sel = (int)p2 - GetClientTop(wnd);
        if (*TextLine(wnd, sel) != LINE)
            SendMessage(wnd, LB_CHOOSE, wnd->selection, 0);
    }
    else    {
        WINDOW pwnd = GetParent(wnd);
        if (GetClass(pwnd) == MENUBAR && (int)p2==GetTop(pwnd))
            return FALSE;
        if ((int)p1 == GetLeft(pwnd)+2)
            return FALSE;
        SendMessage(wnd, CLOSE_WINDOW, 0, 0);
        return TRUE;
    }
    return FALSE;
}

/* --------- PAINT Message -------- */
static void PaintMsg(WINDOW wnd)
{
    int wd;
    unsigned char sep[80], *cp = sep;
    unsigned char sel[80];
    struct PopDown *ActivePopDown;
    struct PopDown *pd1;

    ActivePopDown = pd1 = wnd->mnu->Selections;
    wd = MenuWidth(ActivePopDown)-2;
    while (wd--)
        *cp++ = LINE;
    *cp = '\0';
    SendMessage(wnd, CLEARTEXT, 0, 0);
    wnd->selection = wnd->mnu->Selection;
    while (pd1->SelectionTitle != NULL)    {
        if (*pd1->SelectionTitle == LINE)
            SendMessage(wnd, ADDTEXT, (PARAM) sep, 0);
        else    {
            int len;
            memset(sel, '\0', sizeof sel);
            if (pd1->Attrib & INACTIVE)
                /* ------ inactive menu selection ----- */
                sprintf(sel, "%c%c%c",
                    CHANGECOLOR,
                    wnd->WindowColors [HILITE_COLOR] [FG]|0x80,
                    wnd->WindowColors [STD_COLOR] [BG]|0x80);
            strcat(sel, " ");
            if (pd1->Attrib & CHECKED)
                /* ---- paint the toggle checkmark ---- */
                sel[strlen(sel)-1] = CHECKMARK;
            len=CopyCommand(sel+strlen(sel),pd1->SelectionTitle,
                    pd1->Attrib & INACTIVE,
                    wnd->WindowColors [STD_COLOR] [BG]);
            if (pd1->Accelerator)    {
                /* ---- paint accelerator key ---- */
                int i;
                int wd1 = 2+SelectionWidth(ActivePopDown) -
                                    strlen(pd1->SelectionTitle);
                for (i = 0; keys[i].keylabel; i++)    {
                    if (keys[i].keycode == pd1->Accelerator)   {
                        while (wd1--)
                            strcat(sel, " ");
                        sprintf(sel+strlen(sel), "[%s]",
                            keys[i].keylabel);
                        break;
                    }
                }
            }
            if (pd1->Attrib & CASCADED)    {
                /* ---- paint cascaded menu token ---- */
                if (!pd1->Accelerator)    {
                    wd = MenuWidth(ActivePopDown)-len+1;
                    while (wd--)
                        strcat(sel, " ");
                }
                sel[strlen(sel)-1] = CASCADEPOINTER;
            }
            else
                strcat(sel, " ");
            strcat(sel, " ");
            sel[strlen(sel)-1] = RESETCOLOR;
            SendMessage(wnd, ADDTEXT, (PARAM) sel, 0);
        }
        pd1++;
    }
}

/* ---------- BORDER Message ----------- */
static int BorderMsg(WINDOW wnd)
{
    int i, rtn = TRUE;
    WINDOW currFocus;
    if (wnd->mnu != NULL)    {
        currFocus = inFocus;
        inFocus = NULL;
        rtn = BaseWndProc(POPDOWNMENU, wnd, BORDER, 0, 0);
        inFocus = currFocus;
        for (i = 0; i < ClientHeight(wnd); i++)    {
            if (*TextLine(wnd, i) == LINE)    {
                wputch(wnd, LEDGE, 0, i+1);
                wputch(wnd, REDGE, WindowWidth(wnd)-1, i+1);
            }
        }
    }
    return rtn;
}

/* -------------- LB_CHOOSE Message -------------- */
static void LBChooseMsg(WINDOW wnd, PARAM p1)
{
    struct PopDown *ActivePopDown = wnd->mnu->Selections;
    if (ActivePopDown != NULL)    {
        int *attr = &(ActivePopDown+(int)p1)->Attrib;
        wnd->mnu->Selection = (int)p1;
        if (!(*attr & INACTIVE))    {
            if (*attr & TOGGLE)
                *attr ^= CHECKED;
            PostMessage(GetParent(wnd), COMMAND,
                (ActivePopDown+(int)p1)->ActionId, p1);
        }
        else
            beep();
    }
}

/* ---------- KEYBOARD Message --------- */
static BOOL KeyboardMsg(WINDOW wnd, PARAM p1, PARAM p2)
{
    struct PopDown *ActivePopDown = wnd->mnu->Selections;
    if (wnd->mnu != NULL)    {
        if (ActivePopDown != NULL)    {
            int c = (int)p1;
            int sel = 0;
            int a;
            struct PopDown *pd = ActivePopDown;

            if ((c & OFFSET) == 0)
                c = tolower(c);
            a = AltConvert(c);

            while (pd->SelectionTitle != NULL)    {

                char *cp = strchr(pd->SelectionTitle,
                                SHORTCUTCHAR);
                int sc = tolower(*(cp+1));
                if ((cp && sc == c) ||
                        (a && sc == a) ||
                            pd->Accelerator == c)    {
                    PostMessage(wnd, LB_SELECTION, sel, 0);
                    PostMessage(wnd, LB_CHOOSE, sel, TRUE);
                    return TRUE;
                }
                pd++, sel++;
            }
        }
    }
    switch ((int)p1)    {
        case F1:
            if (ActivePopDown == NULL)
                SendMessage(GetParent(wnd), KEYBOARD, p1, p2);
            else
                DisplayHelp(wnd,
                    (ActivePopDown+wnd->selection)->help);
            return TRUE;
        case ESC:
            SendMessage(wnd, CLOSE_WINDOW, 0, 0);
            return TRUE;
        case FWD:
        case BS:
            if (GetClass(GetParent(wnd)) == MENUBAR)
                PostMessage(GetParent(wnd), KEYBOARD, p1, p2);
            return TRUE;
        case UP:
            if (wnd->selection == 0)    {
                if (wnd->wlines == ClientHeight(wnd))    {
                    PostMessage(wnd, LB_SELECTION,
                                    wnd->wlines-1, FALSE);
                    return TRUE;
                }
            }
            break;
        case DN:
            if (wnd->selection == wnd->wlines-1)    {
                if (wnd->wlines == ClientHeight(wnd))    {
                    PostMessage(wnd, LB_SELECTION, 0, FALSE);
                    return TRUE;
                }
            }
            break;
        case HOME:
        case END:
        case '\r':
            break;
        default:
            return TRUE;
    }
    return FALSE;
}

/* ----------- CLOSE_WINDOW Message ---------- */
static int CloseWindowMsg(WINDOW wnd)
{
    int rtn;
    SendMessage(wnd, RELEASE_MOUSE, 0, 0);
    SendMessage(wnd, RELEASE_KEYBOARD, 0, 0);
    SendMessage(NULL, RESTORE_CURSOR, 0, 0);
    rtn = BaseWndProc(POPDOWNMENU, wnd, CLOSE_WINDOW, 0, 0);
    SendMessage(GetParent(wnd), CLOSE_POPDOWN, 0, 0);
    return rtn;
}

/* - Window processing module for POPDOWNMENU window class - */
int PopDownProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    switch (msg)    {
        case CREATE_WINDOW:
            return CreateWindowMsg(wnd);
        case LEFT_BUTTON:
            LeftButtonMsg(wnd, p1, p2);
            return FALSE;
        case DOUBLE_CLICK:
            return TRUE;
        case LB_SELECTION:
            if (*TextLine(wnd, (int)p1) == LINE)
                return TRUE;
            wnd->mnu->Selection = (int)p1;
            break;
        case BUTTON_RELEASED:
            if (ButtonReleasedMsg(wnd, p1, p2))
                return TRUE;
            break;
        case BUILD_SELECTIONS:
            wnd->mnu = (void *) p1;
            wnd->selection = wnd->mnu->Selection;
            break;
        case PAINT:
            if (wnd->mnu == NULL)
                return TRUE;
            PaintMsg(wnd);
            break;
        case BORDER:
            return BorderMsg(wnd);
        case LB_CHOOSE:
            LBChooseMsg(wnd, p1);
            return TRUE;
        case KEYBOARD:
            if (KeyboardMsg(wnd, p1, p2))
                return TRUE;
            break;
        case CLOSE_WINDOW:
            return CloseWindowMsg(wnd);
        default:
            break;
    }
    return BaseWndProc(POPDOWNMENU, wnd, msg, p1, p2);
}

/* --------- compute menu height -------- */
int MenuHeight(struct PopDown *pd)
{
    int ht = 0;
    while (pd[ht].SelectionTitle != NULL)
        ht++;
    return ht+2;
}

/* --------- compute menu width -------- */
int MenuWidth(struct PopDown *pd)
{
    int wd = 0, i;
    int len = 0;

    wd = SelectionWidth(pd);
    while (pd->SelectionTitle != NULL)    {
        if (pd->Accelerator)    {
            for (i = 0; keys[i].keylabel; i++)
                if (keys[i].keycode == pd->Accelerator)    {
                    len = max(len, 2+strlen(keys[i].keylabel));
                    break;
                }
        }
        if (pd->Attrib & CASCADED)
            len = max(len, 2);
        pd++;
    }
    return wd+5+len;
}

/* ---- compute the maximum selection width in a menu ---- */
static int SelectionWidth(struct PopDown *pd)
{
    int wd = 0;
    while (pd->SelectionTitle != NULL)    {
        int len = strlen(pd->SelectionTitle)-1;
        wd = max(wd, len);
        pd++;
    }
    return wd;
}

/* ----- copy a menu command to a display buffer ---- */
int CopyCommand(unsigned char *dest, unsigned char *src,
                                        int skipcolor, int bg)
{
    unsigned char *d = dest;
    while (*src && *src != '\n')    {
        if (*src == SHORTCUTCHAR)    {
            src++;
            if (!skipcolor)    {
                *dest++ = CHANGECOLOR;
                *dest++ = cfg.clr[POPDOWNMENU]
                            [HILITE_COLOR] [BG] | 0x80;
                *dest++ = bg | 0x80;
                *dest++ = *src++;
                *dest++ = RESETCOLOR;
            }
        }
        else
            *dest++ = *src++;
    }
    return (int) (dest - d);
}






<a name="00f9_000f">
<a name="00f9_0010">
[LISTING THREE]
<a name="00f9_0010">

/* ------------- menu.c ------------- */

#include "dflat.h"

static struct PopDown *FindCmd(MBAR *mn, int cmd)
{
    MENU *mnu = mn->PullDown;
    while (mnu->Title != (void *)-1)    {
        struct PopDown *pd = mnu->Selections;
        while (pd->SelectionTitle != NULL)    {
            if (pd->ActionId == cmd)
                return pd;
            pd++;
        }
        mnu++;
    }
    return NULL;
}

char *GetCommandText(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        return pd->SelectionTitle;
    return NULL;
}

BOOL isCascadedCommand(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        return pd->Attrib & CASCADED;
    return FALSE;
}

void ActivateCommand(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        pd->Attrib &= ~INACTIVE;
}

void DeactivateCommand(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        pd->Attrib |= INACTIVE;
}

BOOL isActive(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        return !(pd->Attrib & INACTIVE);
    return FALSE;
}

BOOL GetCommandToggle(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        return (pd->Attrib & CHECKED) != 0;
    return FALSE;
}

void SetCommandToggle(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        pd->Attrib |= CHECKED;
}

void ClearCommandToggle(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        pd->Attrib &= ~CHECKED;
}

void InvertCommandToggle(MBAR *mn, int cmd)
{
    struct PopDown *pd = FindCmd(mn, cmd);
    if (pd != NULL)
        pd->Attrib ^= CHECKED;
}






<a name="00f9_0011">
<a name="00f9_0012">
[LISTING FOUR]
<a name="00f9_0012">


/* ------------- sysmenu.c ------------ */

#include "dflat.h"

int SystemMenuProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
{
    int mx, my;
    WINDOW wnd1;
    switch (msg)    {
        case CREATE_WINDOW:
            wnd->holdmenu = ActiveMenuBar;
            ActiveMenuBar = &SystemMenu;
            SystemMenu.PullDown[0].Selection = 0;
            break;
        case LEFT_BUTTON:
            wnd1 = GetParent(wnd);
            mx = (int) p1 - GetLeft(wnd1);
            my = (int) p2 - GetTop(wnd1);
            if (HitControlBox(wnd1, mx, my))
                return TRUE;
            break;
        case LB_CHOOSE:
            PostMessage(wnd, CLOSE_WINDOW, 0, 0);
            break;
        case DOUBLE_CLICK:
            if (p2 == GetTop(GetParent(wnd)))    {
                PostMessage(GetParent(wnd), msg, p1, p2);
                SendMessage(wnd, CLOSE_WINDOW, TRUE, 0);
            }
            return TRUE;
        case SHIFT_CHANGED:
            return TRUE;
        case CLOSE_WINDOW:
            ActiveMenuBar = wnd->holdmenu;
            break;
        default:
            break;
    }
    return DefaultWndProc(wnd, msg, p1, p2);
}

/* ------- Build a system menu -------- */
void BuildSystemMenu(WINDOW wnd)
{
    int lf = GetLeft(wnd)+1;
    int tp = GetTop(wnd)+1;
    int ht = MenuHeight(SystemMenu.PullDown[0].Selections);
    int wd = MenuWidth(SystemMenu.PullDown[0].Selections);
    WINDOW SystemMenuWnd;

    SystemMenu.PullDown[0].Selections[6].Accelerator =
        (GetClass(wnd) == APPLICATION) ? ALT_F4 : CTRL_F4;

    if (lf+wd > SCREENWIDTH-1)
        lf = (SCREENWIDTH-1) - wd;
    if (tp+ht > SCREENHEIGHT-2)
        tp = (SCREENHEIGHT-2) - ht;

    SystemMenuWnd = CreateWindow(POPDOWNMENU, NULL,
                lf,tp,ht,wd,NULL,wnd,SystemMenuProc, 0);

#ifdef INCLUDE_RESTORE
    if (wnd->condition == ISRESTORED)
        DeactivateCommand(&SystemMenu, ID_SYSRESTORE);
    else
        ActivateCommand(&SystemMenu, ID_SYSRESTORE);
#endif

    if (TestAttribute(wnd, MOVEABLE)
#ifdef INCLUDE_MAXIMIZE
            && wnd->condition != ISMAXIMIZED
#endif
                )
        ActivateCommand(&SystemMenu, ID_SYSMOVE);
    else
        DeactivateCommand(&SystemMenu, ID_SYSMOVE);

    if (wnd->condition != ISRESTORED ||
            TestAttribute(wnd, SIZEABLE) == FALSE)
        DeactivateCommand(&SystemMenu, ID_SYSSIZE);
    else
        ActivateCommand(&SystemMenu, ID_SYSSIZE);

#ifdef INCLUDE_MINIMIZE
    if (wnd->condition == ISMINIMIZED ||
            TestAttribute(wnd, MINMAXBOX) == FALSE)
        DeactivateCommand(&SystemMenu, ID_SYSMINIMIZE);
    else
        ActivateCommand(&SystemMenu, ID_SYSMINIMIZE);
#endif

#ifdef INCLUDE_MAXIMIZE
    if (wnd->condition != ISRESTORED ||
            TestAttribute(wnd, MINMAXBOX) == FALSE)
        DeactivateCommand(&SystemMenu, ID_SYSMAXIMIZE);
    else
        ActivateCommand(&SystemMenu, ID_SYSMAXIMIZE);
#endif

    SendMessage(SystemMenuWnd, BUILD_SELECTIONS,
                (PARAM) &SystemMenu.PullDown[0], 0);
    SendMessage(SystemMenuWnd, SHOW_WINDOW, 0, 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.