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++

Building a D-Flat Application


OCT91: C PROGRAMMING

This month we continue the D-Flat project by publishing the last of the supporting source code modules. Two files provide functions that D-Flat uses to manage lists and screen rectangles, and two other files describe the messages and commands that D-Flat and its applications use. For the first time, we will see an example D-Flat application program. Next month we start looking at the modules that describe and manage the window classes.

Linked Lists in D-Flat

Listing One, page 166, is lists.c, the source file that maintains two linked lists of windows. The first linked list, Built, records the D-Flat application's windows in the sequence in which they are built. The second linked list, Focus, records the windows in the order in which they take the user's input focus.

Linked lists are managed by a structure that contains two pointers to window structures: a FirstWindow pointer and a LastWindow pointer. Every window structure has nextfocus, prevfocus, nextbuilt, and prevbuilt pointers. The LinkedList structure is the list head, and the window structure contains the forward and reverse linked list pointers. These structures are defined in dflat.h.

There are two linked lists because some processes need to traverse the windows in the sequence in which they have been in focus, and others need to use the sequence in which the windows were created.

The functions named SetNextFocus and SetPrevFocus set the focus to the next or previous window in the Focus linked list. The wnd parameter is the window from which the functions start looking. These functions are called when windows close or when the user steps through the windows with the Alt+F6 key or the Tab key on a dialog box. The SetNextFocus function attempts to find a window that has the same parent as the one from which the focus is being taken. It uses the SearchFocus-Next function to do that.

Four functions add and remove windows from the two linked lists. They are AppendBuiltWindow, RemoveBuiltWindow, AppendFocusWindow, and RemoveFocus Window.

There are four functions for traversing the lists in a first-to-last sequence. A program calls GetFirstChild or GetFirstFocusChild to get the first child window for a parent window from one of the two lists. Then it calls GetNextChild or GetNextFocusChild successively until one of these functions returns NULL. Example 1 shows the code sequence for traversing a list. In this example, pwnd is the parent window, and cwnd is each of the child windows in turn.

Example 1: The code sequence for traversing a list

  WINDOW cwnd = GetFirstChild (pwnd);
  while (cwnd != NULLWND) {
     /*--process the child window--*/
     cwnd = GetNextChild (pwnd, cwnd);
  }

There are two functions for traversing the Built linked list in reverse order. They are GetLastChild and GetPrevChild. They work the same as their forward counterparts. No functions exist to traverse the Focus list because there is no need for them.

The SkipSystemWindows function is called to bypass granting the focus to APPLICATION, MENUBAR, and STATUS-BAR windows when the user is using Alt+F6 or another method to move among the document windows.

Rectangles

To properly manage screen displays, D-Flat needs some rectangle management functions. Listing Two, page 167, is rect.c, the source file that contains these rectangle functions. The first function is subVector, which is used only by the other functions in rect.c. The function accepts two vectors, described by their integer end points as tl, t2 and o1, o2. It computes the overlap of the two vectors and stores the result as two end points in the integers pointed to by the v1 and v2 integer pointer parameters. The function uses the within macro defined in rect.h, which I published several months ago. That header file also defines the RECT typedef, which is a structure that contains the left, top, right, and bottom rectangle coordinates.

The subRectangle function accepts two RECTs and computes the rectangle resulting from the overlap of the two, returning the computed rectangle in a RECT structure.

The ClientRect function computes the client rectangle of a specified window by using the four macros, GetClientLeft, GetClientRight, GetClientTop, and GetClientBottom. These macros are defined in dflat.h. They compute the client rectangle from the window's rectangle by adjusting for the window's border, titlebar, and menubar, if they exist.

The Relative WindowRect function produces a rectangle that is relative to 0 within the rectangle of the specified window.

The ClipRectangle function clips a rectangle that is inside a window so that the rectangle does not extend beyond the screen borders or the borders of any ancestor windows.

D-Flat Messages

Several months ago I published the messages.h file that defined the D-Flat messages as members of an enum definition. That technique is changed. Listing Three, page 167, is dflatmsg.h, a new header file that contains the messages expressed as parameters to the DFlatMsg macro. Parts of the program that need lists of the messages in different formats define the macro to their own needs and include the dflatmsg.h file. For example, the enumerated list of messages in dflat.h now works like Example 2. The DFlatMsg macro gets redefined to use the message values as members of the enumerated list. The MESSAGE-COUNT entry represents the last entry in the list and is, therefore, the number of messages in the system.

Example 2: The enumerated list of messages in dflat.h works like this.

  typedef enum messages {
    #undef DFlatMsg
    #define DFlatMsg(m) m,
    #include "dflatmsg.h"
    MESSAGECOUNT
  } MESSAGE;

The message logging process needs strings of the message's names so that it can display messages in the log window. It uses dflatmsg.h, as in Example 3. The DFlatMsg macro gets redefined to use the message values as strings with one leading space. This usage allows the messages to appear in a multiple-selection list box.

Example 3: The message logging process uses strings of the message's names to display messages in the log window.

  static char *message[] = {
      #undef DFlatMsg
      #define DFlatMsg(m) " " #m,
      #include "dflatmsg.h"
      NULL
  };

D-Flat Commands

One of the D-Flat standard messages is the COMMAND message. A program sends that message to a window with this format:

SendMessage(wnd, COMMAND, cmdcode, 0);

The command code is a unique value that the window must recognize. The menu system uses command codes to tell the window which menu command was executed. The dialog box system identifies control windows with command codes. Listing Four, page 167, is commands.h, the file that specifies the commands in an enumerated list. As published, the list has all the commands used by D-Flat and the memopad application. You will add entries to this list to add custom commands for your application.

An Example Program: MEMOPAD

Listing Five, page 169, is memopad.c, an example of an application that uses D-Flat. It is a multiple-document notepad program that uses all the standard D-Flat menus and dialog boxes and lets you view and modify many different text files at once. Its architecture is typical of a D-Flat application program.

The main function in memopad.c begins by calling the init_messages function to initialize message processing. Then it stores its argv parameter in an external variable so that other parts of the program -- specifically the module that loads the help database -- can determine the subdirectory from which the program is run.

The memopad program now creates its application window by calling the CreateWindow function. The APPLICATION argument tells D-Flat the window class. The next argument is the window's title. Following that are the window's upper-left coordinates and height and width. By using 0,0 as the coordinates and -1, -1 as the dimensions, the program tells D-Flat to use the entire screen for the window. The MainMenu argument is the address of the MENU structure for the application window's menu bar. The next argument is NULL. That argument usually specifies the WINDOW handle of the window's parent, but an APPLICATION window has no parent. The MemoPadProc argument is the address of the window processing module that will receive messages sent to the window. An APPLICATION window has a default window processing module, but each application must provide an additional one to handle the commands that are unique to the application. The last argument to the CreateWindow function is the window's attribute word, which consists of attribute values ORed together. The memopad application window must be movable and sizable, and have a border and a status bar.

After the window is created, the program sends it a message telling it to take the user's input focus. Then the program scans the command line to see if the user named any files. If so, each file specification is sent in turn to the PadWindow function.

The next step is for the program to enter the message dispatching loop. It does this by successively calling the dispatch_message function until the function returns a false value, at which time the program is finished and may terminate.

The PadWindow function accepts a file specification. The function expands the specification into filenames one at a time and sends the filenames to the OpenPadWindow function.

The MemoPadProc function is the window processing module for the memopad application window. When the system or a window sends a message to the application window, D-Flat will execute this function, passing it the message. It does so because the function's address was included in the Create Window call that created the window.

A window processing module can process any message. Usually, however, the window processing module for the application will only process COMMAND messages that the menu system sends when the user chooses a menu command and when the current document window -- if one exists -- does not intercept then message. MemoPadProc processes those COMMAND messages unique to the memopad application and not processed by the document windows. The ID_NEW command is to create a new file in a new document window. The ID_OPEN command is to select an existing file to load into a new document window. The ID_SAVE and ID_SAVEAS commands are to save the document window that has the focus back to disk. ID_DELETEFILE is to delete the file represented by the window that has the focus. ID_PRINT prints the current document, and ID_ABOUT displays a message about the application.

When MemoPadProc processes a message, it can simply return a TRUE value. This action prevents any further processing of the message by the default window processing module for the APPLICATION class. If MemoPadProc does not want to intercept and process a message, it calls the DefaultWndProc function, which passes the message to the default window processing module for the window's class, in this case the APPLICATION class.

The NewFile and SelectFile functions both call OpenPadWindow to open a new document window. NewFile passes "Untitled" as the window's title. SelectFile executes the File Open dialog box by calling the OpenFileDialogBox function. If that function returns a true value, the user has selected a file specification which the function stored in the FileName character array. The SelectFile function searches the windows that are the children of the memopad application window to see if the selected file is already loaded into a document window. If so, SelectFile gives the focus to that window and returns. Otherwise, SelectFile calls OpenPadWindow, passing it the file specification of the selected file.

The OpenPadWindow function opens a document window and, if the specified file is "Untitled," initializes the window with a blank document. Otherwise it loads the text file into the window. First, the OpenPadWindow function uses the stat function to see if the file exists and, if it does, to determine its size. If the file does not exist, OpenPad Window calls the ErrorMessage function to display an error message to that effect. If the file does exist, or if an untitled new document is being built, OpenPadWindow calls CreateWindow to create the document window. The title of the window comes from the filename component of the file specification. The window position is computed so that successive windows are cascaded. The application window is the parent of the document window, and EditorProc is the document window's window processing module. After creating the document window, OpenPadWindow calls LoadFile to load the specified file into the window and then sets the user input focus to the document window.

Take careful notice of what has just happened. The program creates an application window and then goes into the dispatch_message loop waiting for the loop to return false, whereupon the program terminates. That simple logic is the foundation of event-driven, message-based programming. Everything else that happens in a D-Flat program is the result of messages sent to the application window. Other than for the time-of-day display in the status bar, the program will do nothing until the user takes an action that generates a message. When the MemoPadProc function receives one of the COMMAND messages that it recognizes, things begin to happen. Until one of those commands comes along, the user can move and resize the application window, change options, and read the help screens, but nothing of significance will occur within the application itself until MemoPadProc gets a message.

The LoadFile function loads a specified text file into a document window. First the function allocates a buffer that can contain the text of the file. Then the function reads the file into the buffer and sends the SETTEXT message to the window to tell it to use the buffer for its text display.

The PrintPad function prints the contents of the current document window by sending it to the standard print device. If you were going to use this memopad program for serious text editing, you would want to put tests for printer ready into this function and then intercept critical errors in case the printer runs out of paper or otherwise fails while you are printing. Without these measures, DOS will splash its rude error messages across your orderly D-Flat screens.

The SaveFile function saves the text in the current document window to a disk file. If the document window is untitled, or if the user selected the Save As command on the File menu, the SaveFile function executes the Save As dialog box by calling the SaveAsDialogBox function. If that function returns false, the user has neglected to specify a filename for the document, and the SaveFile function returns without doing anything. Otherwise, the SaveFile function assigns the filename to the document window and uses the filename component of the new filename as the new title of the document window. Next, the SaveFile function writes the text to the disk file. Before opening the file, the SaveFile function calls the MomentaryMessage function to display a small message in a window on the screen. This tells the user that something is going on and to stand by. After closing the file, the SaveFile function sends the CLOSE_WINDOW message to the momentary message window.

The DeleteFile function deletes the file that is in the current document window and closes that window. First, it calls the YesNoBox function to allow the user to confirm the delete.

The ShowPosition function is an example of how an application can display one-liners in the application window's status bar. It builds a text display that contains the line and column numbers of where the keyboard cursor is in the current document window. Then it sends the ADDSTATUS message to the application window with the address of the text display as the first parameter in the message.

The EditorProc function is the window processing module for the document windows. Every message sent to one of these windows comes to this function first. Observe its treatment of the SETFOCUS and KEYBOARD_CURSOR messages. When a window is to take the focus, it receives the SETFOCUS message with the first parameter set to true. When a window is to give up the focus, it receives the SETFOCUS message with the first parameter set to false. The EditorProc function uses this message and the KEYBOARD_CURSOR message to manage the continuing line/column display in the status bar. Whenever the keyboard cursor is to change, the current window receives the KEYBOARD_CURSOR message with the column coordinate as the first parameter and the row coordinate as the second parameter. These messages cause the EditorProc function to call the ShowPosition function to update the status bar display to the new line and column. When the document window gives up the focus, the EditorProc function sends the ADDSTATUS message to the application window with zero parameters to tell the application window to clear the text display in the status bar. The SETFOCUS and KEYBOARD cursor messages both call the DefaultWndProc function before they do their processing. This is the procedure that a window processing module uses to cause the default message processing to occur first. The module must then return the value returned from the DefaultWndProc function when the module is done with its own processing.

The EditorProc function intercepts the CLOSE_WINDOW function to see if the user has made any changes to the text without saving it. If so, the function calls the YesNoBox function to allow the user to save the text. If the user chooses to save the text, the EditorProc function sends a COMMAND message with the ID_SAVE command to the application window. This is an example of how the document window simulates a user action by sending a COMMAND to the application as if the user had chosen the command from a menu.

Any messages that EditorProc does not intercept get sent to the default window processing module for the EDITBOX class of windows when EditorProc calls DefaultWndProc before EditorProc returns.

Take another moment to consider again what has happened. Before the user chose a new untitled document window or loaded a file into a document window, the program was looping and waiting for something to do. The user's menu selection sent a message to the application window. The application window created a document window and gave it the focus. Now almost every thing the user does results in a message to the in-focus document window. The document window is an EDITBOX, so all the messages that support an EDITBOX go to the document window. The EDITBOX's default window processing module manages the details of text editing, and the application's edit box processing module processes only those messages that are unique to the application.

The underlying concepts of event-driven, message-based processing are what the memopad application program employs. These same concepts permeate the D-Flat library itself. Help, options, menus, dialog boxes, the status bar, and text editing all use the same mechanisms that an application will use. They encapsulate the common processes that most applications use into a user interface library. The applications software can be trivial by comparison. The memopad program is implemented in a relatively small source code module -- less than 400 lines of code -- yet it is a multiple-document text editor program, something that would be a significant software development effort if you had to build the user interface and the text editing functions into the application itself.

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. Its name is DFLATn.ARC, where the n is an integer that represents a loosely-assigned version number. There is another file, named DFnTXT.ARC, which contains a description of what has changed and how to build the software, the Help system database, and the documentation for the programmer's API. At present, everything compiles and works with Turbo C 2.0, Turbo C++, and Microsoft C 6.0. There is a makefile for the TC and MSC compilers. There is an example program, the MEMOPAD program, which is a multiple-document notepad.

If for some reason you cannot get to either online service, send me a formatted disk -- any PC format -- and an addressed, stamped diskette mailer. Send it to me in care of DDJ. I'll send you the latest copy of the library. The software is free, but if you care to, stick a dollar bill in the mailer. I'll match the dollar and give it to the Brevard County Food Bank. They take care of homeless and hungry children.

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

A Star is Born

Philippe Kahn has a video. You won't see it on MTV, and I'm not sure how you get it. It's called "The World of Objects," and it's mostly Philippe explaining what object-oriented programming is, and why it is the answer to a programmer's prayer. It begins with Philippe playing the soprano sax, Philippe playing the piano, Philippe playing the bass violin, and Philippe playing the drums. If you can make it past that, you are treated to a session with Philippe's band, a ride with Philippe in his 1960 Corvette, and an introduction to his dog and her puppies. I liked the puppies.

Next comes an entertaining lecture delivered by Philippe using the Gene Autry school of method acting and some horrible phrases like, "state of Corvetteness," "art of car driving," and "well-architected." The best part was a clamshell, pen-based, multimedia, notebook computer with high-resolution color graphics and no keyboard. Where do I get one of those? I think it was a fake.

The show includes cameo appearances by Alan Kay, Marvin Minsky, Bjarne Stroustrup, Joseph Weizenbaum, and Niklaus Wirth, all telling us that OOP is wonderful, but none telling us what it is. Niklaus didn't seem as committed as the others. Alan warned us to get on board or be left in the dust.

I had trouble identifying the audience for the video. It attempts to explain OOP, but to whom? Nonprogrammers have no idea why there are different orientations to programming because they do not know what programming is in the first place. Programmers are not going to associate with the non-programming analogies to cars, music, and animals. Everyone makes that mistake. OOP is hard to explain, so those in the know search for other-world metaphors, which never quite make it. Even Minsky used couches and chairs.

Judy and I watched the video together. Judy is a programmer who did not know about OOP before seeing this show. She still isn't sure what it is, but she had this comment about the video: "Except for the mother dog, there are no women in it."


_C PROGRAMMING COLUMN_
by Al Stevens


[LISTING ONE]
<a name="0248_000f">

/* --------------- lists.c -------------- */

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


struct LinkedList Focus = {NULLWND, NULLWND};
struct LinkedList Built = {NULLWND, NULLWND};

/* --- set focus to the window beneath the one specified --- */
void SetPrevFocus(WINDOW wnd)
{
    if (wnd != NULLWND && wnd == inFocus)    {
        WINDOW wnd1 = wnd;
        while (TRUE)    {
            if ((wnd1 = PrevWindow(wnd1)) == NULLWND)
                wnd1 = Focus.LastWindow;
            if (wnd1 == wnd)
                return;
            if (wnd1 != NULLWND)
                break;
        }
        if (wnd1 != NULLWND)
            SendMessage(wnd1, SETFOCUS, TRUE, 0);
    }
}

/* this function assumes that wnd is in the Focus linked list */
static WINDOW SearchFocusNext(WINDOW wnd, WINDOW pwnd)
{
    WINDOW wnd1 = wnd;

    if (wnd != NULLWND)    {
        while (TRUE)    {
            if ((wnd1 = NextWindow(wnd1)) == NULLWND)
                wnd1 = Focus.FirstWindow;
            if (wnd1 == wnd)
                return NULLWND;
            if (wnd1 != NULLWND)
                if (pwnd == NULLWND || pwnd == GetParent(wnd1))
                    break;
        }
    }
    return wnd1;
}

/* ----- set focus to the next sibling ----- */
void SetNextFocus(WINDOW wnd)
{
    WINDOW wnd1;

    if (wnd != inFocus)
        return;
    if ((wnd1 = SearchFocusNext(wnd, GetParent(wnd)))==NULLWND)
        wnd1 = SearchFocusNext(wnd, NULLWND);
    if (wnd1 != NULLWND)
        SendMessage(wnd1, SETFOCUS, TRUE, 0);
}

/* ---- remove a window from the Built linked list ---- */
void RemoveBuiltWindow(WINDOW wnd)
{
    if (wnd != NULLWND)    {
        if (PrevWindowBuilt(wnd) != NULLWND)
            NextWindowBuilt(PrevWindowBuilt(wnd)) =
                NextWindowBuilt(wnd);
        if (NextWindowBuilt(wnd) != NULLWND)
            PrevWindowBuilt(NextWindowBuilt(wnd)) =
                PrevWindowBuilt(wnd);
        if (wnd == Built.FirstWindow)
            Built.FirstWindow = NextWindowBuilt(wnd);
        if (wnd == Built.LastWindow)
            Built.LastWindow = PrevWindowBuilt(wnd);
    }
}

/* ---- remove a window from the Focus linked list ---- */
void RemoveFocusWindow(WINDOW wnd)
{
    if (wnd != NULLWND)    {
        if (PrevWindow(wnd) != NULLWND)
            NextWindow(PrevWindow(wnd)) = NextWindow(wnd);
        if (NextWindow(wnd) != NULLWND)
            PrevWindow(NextWindow(wnd)) = PrevWindow(wnd);
        if (wnd == Focus.FirstWindow)
            Focus.FirstWindow = NextWindow(wnd);
        if (wnd == Focus.LastWindow)
            Focus.LastWindow = PrevWindow(wnd);
    }
}

/* ---- append a window to the Built linked list ---- */
void AppendBuiltWindow(WINDOW wnd)
{
    if (wnd != NULLWND)    {
        if (Built.FirstWindow == NULLWND)
            Built.FirstWindow = wnd;
        if (Built.LastWindow != NULLWND)
            NextWindowBuilt(Built.LastWindow) = wnd;
        PrevWindowBuilt(wnd) = Built.LastWindow;
        NextWindowBuilt(wnd) = NULLWND;
        Built.LastWindow = wnd;
    }
}

/* ---- append a window to the Focus linked list ---- */
void AppendFocusWindow(WINDOW wnd)
{
    if (wnd != NULLWND)    {
        if (Focus.FirstWindow == NULLWND)
            Focus.FirstWindow = wnd;
        if (Focus.LastWindow != NULLWND)
            NextWindow(Focus.LastWindow) = wnd;
        PrevWindow(wnd) = Focus.LastWindow;
        NextWindow(wnd) = NULLWND;
        Focus.LastWindow = wnd;
    }
}

/* -------- get the first child of a parent window ------- */
WINDOW GetFirstChild(WINDOW wnd)
{
    WINDOW ThisWindow = Built.FirstWindow;
    while (ThisWindow != NULLWND)    {
        if (GetParent(ThisWindow) == wnd)
            break;
        ThisWindow = NextWindowBuilt(ThisWindow);
    }
    return ThisWindow;
}

/* -------- get the next child of a parent window ------- */
WINDOW GetNextChild(WINDOW wnd, WINDOW ThisWindow)
{
    if (ThisWindow != NULLWND)    {
        do    {
            if ((ThisWindow = NextWindowBuilt(ThisWindow)) !=
                    NULLWND)
                if (GetParent(ThisWindow) == wnd)
                    break;
        }    while (ThisWindow != NULLWND);
    }
    return ThisWindow;
}

/* -- get first child of parent window from the Focus list -- */
WINDOW GetFirstFocusChild(WINDOW wnd)
{
    WINDOW ThisWindow = Focus.FirstWindow;
    while (ThisWindow != NULLWND)    {
        if (GetParent(ThisWindow) == wnd)
            break;
        ThisWindow = NextWindow(ThisWindow);
    }
    return ThisWindow;
}

/* -- get next child of parent window from the Focus list -- */
WINDOW GetNextFocusChild(WINDOW wnd, WINDOW ThisWindow)
{
    while (ThisWindow != NULLWND)    {
        ThisWindow = NextWindow(ThisWindow);
        if (ThisWindow != NULLWND)
            if (GetParent(ThisWindow) == wnd)
                break;
    }
    return ThisWindow;
}

/* -------- get the last child of a parent window ------- */
WINDOW GetLastChild(WINDOW wnd)
{
    WINDOW ThisWindow = Built.LastWindow;
    while (ThisWindow != NULLWND)    {
        if (GetParent(ThisWindow) == wnd)
            break;
        ThisWindow = PrevWindowBuilt(ThisWindow);
    }
    return ThisWindow;
}

/* ------- get the previous child of a parent window ------- */
WINDOW GetPrevChild(WINDOW wnd, WINDOW ThisWindow)
{
    if (ThisWindow != NULLWND)    {
        do    {
            if ((ThisWindow = PrevWindowBuilt(ThisWindow)) !=
                    NULLWND)
                if (GetParent(ThisWindow) == wnd)
                    break;
        }    while (ThisWindow != NULLWND);
    }
    return ThisWindow;
}

/* --- bypass system windows when stepping through focus --- */
void SkipSystemWindows(int Prev)
{
    int cl, ct = 0;
    while ((cl = GetClass(inFocus)) == MENUBAR ||
            cl == APPLICATION
#ifdef INCLUDE_STATUSBAR
                 || cl == STATUSBAR
#endif
                                )    {
        if (Prev)
            SetPrevFocus(inFocus);
        else
            SetNextFocus(inFocus);
        if (++ct == 3)
            break;
    }
}




<a name="0248_0010">
<a name="0248_0011">
[LISTING TWO]
<a name="0248_0011">

/* ------------- rect.c --------------- */

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

 /* -- Produce vector end points produced by overlap of two other vectors -- */
static void subVector(int *v1, int *v2, int t1, int t2, int o1, int o2)
{
    *v1 = *v2 = -1;
    if (within(o1, t1, t2))    {
        *v1 = o1;
        if (within(o2, t1, t2))
            *v2 = o2;
        else
            *v2 = t2;
    }
    else if (within(o2, t1, t2))    {
        *v2 = o2;
        if (within(o1, t1, t2))
            *v1 = o1;
        else
            *v1 = t1;
    }
    else if (within(t1, o1, o2))    {
        *v1 = t1;
        if (within(t2, o1, o2))
            *v2 = t2;
        else
            *v2 = o2;
    }
    else if (within(t2, o1, o2))    {
        *v2 = t2;
        if (within(t1, o1, o2))
            *v1 = t1;
        else
            *v1 = o1;
    }
}

 /* -- Return rectangle produced by the overlap of two other rectangles --- */
RECT subRectangle(RECT r1, RECT r2)
{
    RECT r = {0,0,0,0};
    subVector((int *) &RectLeft(r), (int *) &RectRight(r),
        RectLeft(r1), RectRight(r1),
        RectLeft(r2), RectRight(r2));
    subVector((int *) &RectTop(r), (int *) &RectBottom(r),
        RectTop(r1), RectBottom(r1),
        RectTop(r2), RectBottom(r2));
    if (RectRight(r) == -1 || RectTop(r) == -1)
        RectRight(r) =
        RectLeft(r) =
        RectTop(r) =
        RectBottom(r) = 0;
    return r;
}

/* ------- return the client rectangle of a window ------ */
RECT ClientRect(void *wnd)
{
    RECT rc;

    RectLeft(rc) = GetClientLeft((WINDOW)wnd);
    RectTop(rc) = GetClientTop((WINDOW)wnd);
    RectRight(rc) = GetClientRight((WINDOW)wnd);
    RectBottom(rc) = GetClientBottom((WINDOW)wnd);
    return rc;
}

/* ---- return the rectangle relative to its window's screen position ---- */
RECT RelativeWindowRect(void *wnd, RECT rc)
{
    RectLeft(rc) -= GetLeft((WINDOW)wnd);
    RectRight(rc) -= GetLeft((WINDOW)wnd);
    RectTop(rc) -= GetTop((WINDOW)wnd);
    RectBottom(rc) -= GetTop((WINDOW)wnd);
    return rc;
}

/* ----- clip a rectangle to the parents of the window ----- */
RECT ClipRectangle(void *wnd, RECT rc)
{
    RECT sr;
    RectLeft(sr) = RectTop(sr) = 0;
    RectRight(sr) = SCREENWIDTH-1;
    RectBottom(sr) = SCREENHEIGHT-1;
    if (!TestAttribute((WINDOW)wnd, NOCLIP))
        while ((wnd = GetParent((WINDOW)wnd)) != NULLWND)
            rc = subRectangle(rc, ClientRect(wnd));
    return subRectangle(rc, sr);
}






<a name="0248_0012">
<a name="0248_0013">
[LISTING THREE]
<a name="0248_0013">

/* ----------- dflatmsg.h ------------ */

/* message foundation file
 * make message changes here
 * other source files will adapt
 */

/* -------------- process communication messages ----------- */
DFlatMsg(START)              /* start message processing     */
DFlatMsg(STOP)               /* stop message processing      */
DFlatMsg(COMMAND)            /* send a command to a window   */
/* -------------- window management messages --------------- */
DFlatMsg(CREATE_WINDOW)      /* create a window              */
DFlatMsg(SHOW_WINDOW)        /* show a window                */
DFlatMsg(HIDE_WINDOW)        /* hide a window                */
DFlatMsg(CLOSE_WINDOW)       /* delete a window              */
DFlatMsg(SETFOCUS)           /* set and clear the focus      */
DFlatMsg(PAINT)              /* paint the window's data space*/
DFlatMsg(BORDER)             /* paint the window's border    */
DFlatMsg(TITLE)              /* display the window's title   */
DFlatMsg(MOVE)               /* move the window              */
DFlatMsg(SIZE)               /* change the window's size     */
DFlatMsg(MAXIMIZE)           /* maximize the window          */
DFlatMsg(MINIMIZE)           /* minimize the window          */
DFlatMsg(RESTORE)            /* restore the window           */
DFlatMsg(INSIDE_WINDOW)      /* test x/y inside a window     */
/* ---------------- clock messages ------------------------- */
DFlatMsg(CLOCKTICK)          /* the clock ticked             */
DFlatMsg(CAPTURE_CLOCK)      /* capture clock into a window  */
DFlatMsg(RELEASE_CLOCK)      /* release clock to the system  */
/* -------------- keyboard and screen messages ------------- */
DFlatMsg(KEYBOARD)           /* key was pressed              */
DFlatMsg(CAPTURE_KEYBOARD) /* capture keyboard into a window */
DFlatMsg(RELEASE_KEYBOARD)   /* release keyboard to system   */
DFlatMsg(KEYBOARD_CURSOR)    /* position the keyboard cursor */
DFlatMsg(CURRENT_KEYBOARD_CURSOR) /*read the cursor position */
DFlatMsg(HIDE_CURSOR)        /* hide the keyboard cursor     */
DFlatMsg(SHOW_CURSOR)        /* display the keyboard cursor  */
DFlatMsg(SAVE_CURSOR)      /* save the cursor's configuration*/
DFlatMsg(RESTORE_CURSOR)     /* restore the saved cursor     */
DFlatMsg(SHIFT_CHANGED)      /* the shift status changed     */
DFlatMsg(WAITKEYBOARD)     /* waits for a key to be released */
/* ---------------- mouse messages ------------------------- */
DFlatMsg(MOUSE_INSTALLED)    /* test for mouse installed     */
DFlatMsg(RIGHT_BUTTON)       /* right button pressed         */
DFlatMsg(LEFT_BUTTON)        /* left button pressed          */
DFlatMsg(DOUBLE_CLICK)       /* left button double-clicked   */
DFlatMsg(MOUSE_MOVED)        /* mouse changed position       */
DFlatMsg(BUTTON_RELEASED)    /* mouse button released        */
DFlatMsg(CURRENT_MOUSE_CURSOR)/* get mouse position          */
DFlatMsg(MOUSE_CURSOR)       /* set mouse position           */
DFlatMsg(SHOW_MOUSE)         /* make mouse cursor visible    */
DFlatMsg(HIDE_MOUSE)         /* hide mouse cursor            */
DFlatMsg(WAITMOUSE)          /* wait until button released   */
DFlatMsg(TESTMOUSE)          /* test any mouse button pressed*/
DFlatMsg(CAPTURE_MOUSE)      /* capture mouse into a window  */
DFlatMsg(RELEASE_MOUSE)      /* release the mouse to system  */
/* ---------------- text box messages ---------------------- */
DFlatMsg(ADDTEXT)            /* add text to the text box     */
DFlatMsg(CLEARTEXT)          /* clear the edit box           */
DFlatMsg(SETTEXT)            /* set address of text buffer   */
DFlatMsg(SCROLL)             /* vertical scroll of text box  */
DFlatMsg(HORIZSCROLL)        /* horizontal scroll of text box*/
/* ---------------- edit box messages ---------------------- */
DFlatMsg(EB_GETTEXT)         /* get text from an edit box    */
DFlatMsg(EB_PUTTEXT)         /* put text into an edit box    */
/* ---------------- menubar messages ----------------------- */
DFlatMsg(BUILDMENU)          /* build the menu display       */
DFlatMsg(SELECTION)          /* menubar selection            */
/* ---------------- popdown messages ----------------------- */
DFlatMsg(BUILD_SELECTIONS)   /* build the menu display       */
DFlatMsg(CLOSE_POPDOWN)    /* tell parent popdown is closing */
/* ---------------- list box messages ---------------------- */
DFlatMsg(LB_SELECTION)       /* sent to parent on selection  */
DFlatMsg(LB_CHOOSE)          /* sent when user chooses       */
DFlatMsg(LB_CURRENTSELECTION)/* return the current selection */
DFlatMsg(LB_GETTEXT)         /* return the text of selection */
DFlatMsg(LB_SETSELECTION)    /* sets the listbox selection   */
/* ---------------- dialog box messages -------------------- */
DFlatMsg(INITIATE_DIALOG)    /* begin a dialog               */
DFlatMsg(ENTERFOCUS)         /* tell DB control got focus    */
DFlatMsg(LEAVEFOCUS)         /* tell DB control lost focus   */
DFlatMsg(ENDDIALOG)          /* end a dialog                 */
/* ---------------- help box messages ---------------------- */
DFlatMsg(DISPLAY_HELP)
/* --------------- application window messages ------------- */
DFlatMsg(ADDSTATUS)





<a name="0248_0014">
<a name="0248_0015">
[LISTING FOUR]
<a name="0248_0015">

/* ---------------- commands.h ----------------- */

/* Command values sent as the first parameter
 * in the COMMAND message
 * Add application-specific commands to this enum
 */

#ifndef COMMANDS_H
#define COMMANDS_H

enum commands {
    /* --------------- File menu ---------------- */
    ID_OPEN,
    ID_NEW,
    ID_SAVE,
    ID_SAVEAS,
    ID_DELETEFILE,
    ID_PRINT,
    ID_DOS,
    ID_EXIT,
    /* --------------- Edit menu ---------------- */
    ID_UNDO,
    ID_CUT,
    ID_COPY,
    ID_PASTE,
    ID_PARAGRAPH,
    ID_CLEAR,
    ID_DELETETEXT,
    /* --------------- Search Menu -------------- */
    ID_SEARCH,
    ID_REPLACE,
    ID_SEARCHNEXT,
    /* -------------- Options menu -------------- */
    ID_INSERT,
    ID_WRAP,
    ID_LOG,
    ID_TABS,
    ID_DISPLAY,
    ID_SAVEOPTIONS,
    /* --------------- Window menu -------------- */
    ID_WINDOW,
    ID_CLOSEALL,
    /* --------------- Help menu ---------------- */
    ID_HELPHELP,
    ID_EXTHELP,
    ID_KEYSHELP,
    ID_HELPINDEX,
    ID_ABOUT,
    ID_LOADHELP,
    /* --------------- System menu -------------- */
    ID_SYSRESTORE,
    ID_SYSMOVE,
    ID_SYSSIZE,
    ID_SYSMINIMIZE,
    ID_SYSMAXIMIZE,
    ID_SYSCLOSE,
    /* ---- FileOpen and SaveAs dialog boxes ---- */
    ID_FILENAME,
    ID_FILES,
    ID_DRIVE,
    ID_PATH,
    /* ----- Search and Replace dialog boxes ---- */
    ID_SEARCHFOR,
    ID_REPLACEWITH,
    ID_MATCHCASE,
    ID_REPLACEALL,
    /* ----------- Windows dialog box ----------- */
    ID_WINDOWLIST,
    /* --------- generic command buttons -------- */
    ID_OK,
    ID_CANCEL,
    ID_HELP,
    /* -------------- TabStops menu ------------- */
    ID_TAB2,
    ID_TAB4,
    ID_TAB6,
    ID_TAB8,
    /* ------------ Display dialog box ---------- */
    ID_BORDER,
    ID_TITLE,
    ID_STATUSBAR,
    ID_TEXTURE,
    ID_COLOR,
    ID_MONO,
    ID_REVERSE,
    ID_25LINES,
    ID_43LINES,
    ID_50LINES,
    /* ------------- Log dialog box ------------- */
    ID_LOGLIST,
    ID_LOGGING,
    /* ------------ HelpBox dialog box ---------- */
    ID_HELPTEXT,
    ID_BACK,
    ID_PREV,
    ID_NEXT
};

#endif






<a name="0248_0016">
<a name="0248_0017">
[LISTING FIVE]
<a name="0248_0017">

/* --------------- memopad.c ----------- */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <dos.h>
#ifndef MSC
#include <dir.h>
#endif
#include "dflat.h"

static char Untitled[] = "Untitled";
static int wndpos = 0;

static int MemoPadProc(WINDOW, MESSAGE, PARAM, PARAM);
static void NewFile(WINDOW);
static void SelectFile(WINDOW);
static void PadWindow(WINDOW, char *);
static void OpenPadWindow(WINDOW, char *);
static void LoadFile(WINDOW, int);
static void PrintPad(WINDOW);
static void SaveFile(WINDOW, int);
static void DeleteFile(WINDOW);
static int EditorProc(WINDOW, MESSAGE, PARAM, PARAM);
static char *NameComponent(char *);
char **Argv;

void main(int argc, char *argv[])
{
    WINDOW wnd;
    init_messages();
    Argv = argv;
    wnd = CreateWindow(APPLICATION,
                        "D-Flat MemoPad " VERSION,
                        0, 0, -1, -1,
                        &MainMenu,
                        NULL,
                        MemoPadProc,
                        MOVEABLE  |
                        SIZEABLE  |
                        HASBORDER |
                        HASSTATUSBAR
                        );

    SendMessage(wnd, SETFOCUS, TRUE, 0);
    while (argc > 1)    {
        PadWindow(wnd, argv[1]);
        --argc;
        argv++;
    }
    while (dispatch_message())
        ;
}
/* ------ open text files and put them into editboxes ----- */
static void PadWindow(WINDOW wnd, char *FileName)
{
    int ax, criterr = 1;
    struct ffblk ff;
    char path[64];
    char *cp;

    CreatePath(path, FileName, FALSE, FALSE);
    cp = path+strlen(path);
    CreatePath(path, FileName, TRUE, FALSE);
    while (criterr == 1)    {
        ax = findfirst(path, &ff, 0);
        criterr = TestCriticalError();
    }
    while (ax == 0 && !criterr)    {
        strcpy(cp, ff.ff_name);
        OpenPadWindow(wnd, path);
        ax = findnext(&ff);
    }
}
/* ----- window processing module for the memopad application window ----- */
static int MemoPadProc(WINDOW wnd,MESSAGE msg,PARAM p1,PARAM p2)
{
    switch (msg)    {
        case COMMAND:
            switch ((int)p1)    {
                case ID_NEW:
                    NewFile(wnd);
                    return TRUE;
                case ID_OPEN:
                    SelectFile(wnd);
                    return TRUE;
                case ID_SAVE:
                    SaveFile(inFocus, FALSE);
                    return TRUE;
                case ID_SAVEAS:
                    SaveFile(inFocus, TRUE);
                    return TRUE;
                case ID_DELETEFILE:
                    DeleteFile(inFocus);
                    return TRUE;
                case ID_PRINT:
                    PrintPad(inFocus);
                    return TRUE;
                case ID_ABOUT:
                    MessageBox(
                         "About D-Flat and the MemoPad",
                        "   ZDDDDDDDDDDDDDDDDDDDDDDD?\n"
                        "   3    \\\   \\\     \    3\n"
                        "   3    [  [  [  [    [    3\n"
                        "   3    [  [  [  [    [    3\n"
                        "   3    [  [  [  [ [  [    3\n"
                        "   3    ___   ___   __     3\n"
                        "   @DDDDDDDDDDDDDDDDDDDDDDDY\n"
                        "D-Flat implements the SAA/CUA\n"
                        "interface in a public domain\n"
                        "C language library originally\n"
                        "published in Dr. Dobb's Journal\n"
                        "    ------------------------ \n"
                        "MemoPad is a multiple document\n"
                        "editor that demonstrates D-Flat");
                    MessageBox(
                        "D-Flat Testers and Friends",
            "Jeff Ratcliff, David Peoples, Kevin Slater,\n"
            "Naor Toledo Pinto, Jeff Hahn, Jim Drash,\n"
            "Art Stricek, John Ebert, George Dinwiddie,\n"
            "Damaian Thorne, Wes Peterson, Thomas Ewald,\n"
            "Mitch Miller, Ray Waters, Jim Drash, Eric\n"
            "Silver, Russ Nelson, Elliott Jackson, Warren\n"
            "Master, H.J. Davey, Jim Kyle, Jim Morris,\n"
            "Andrew Terry, Michel Berube, Bruce Edmondson,\n"
            "Peter Baenziger, Phil Roberge, Willie Hutton,\n"
            "Randy Bradley, Tim Gentry, Lee Humphrey,\n"
            "Larry Troxler, Robert DiFalco, Carl Huff,\n"
            "Vince Rice, Michael Kaufman, Donald Cumming,\n"
            "Ross Wheeler, Lou DiNardo, Keith London, Frank\n"
            "Burleigh, Jason Ward, Skip Key, Sam Gentile,\n"
            "Dana P'Simer, Ken North");
                    return TRUE;
                default:
                    break;
            }
            break;
        default:
            break;
    }
    return DefaultWndProc(wnd, msg, p1, p2);
}
/* --- The New command. Open an empty editor window --- */
static void NewFile(WINDOW wnd)
{
    OpenPadWindow(wnd, Untitled);
}
/* --- The Open... command. Select a file  --- */
static void SelectFile(WINDOW wnd)
{
    char FileName[64];
    if (OpenFileDialogBox("*.PAD", FileName))    {
        /* --- see if the document is already in a window --- */
        WINDOW wnd1 = GetFirstChild(wnd);
        while (wnd1 != NULLWND)    {
            if (stricmp(FileName, wnd1->extension) == 0)    {
                SendMessage(wnd1, SETFOCUS, TRUE, 0);
                SendMessage(wnd1, RESTORE, 0, 0);
                return;
            }
            wnd1 = GetNextChild(wnd, wnd1);
        }
        OpenPadWindow(wnd, FileName);
    }
}
/* --- open a document window and load a file --- */
static void OpenPadWindow(WINDOW wnd, char *FileName)
{
    static WINDOW wnd1 = NULLWND;
    struct stat sb;
    char *Fname = FileName;
    char *ermsg;
    if (strcmp(FileName, Untitled))    {
        if (stat(FileName, &sb))    {
            if ((ermsg = malloc(strlen(FileName)+20)) != NULL) {
                strcpy(ermsg, "No such file as\n");
                strcat(ermsg, FileName);
                ErrorMessage(ermsg);
                free(ermsg);
            }
            return;
        }
        Fname = NameComponent(FileName);
    }
    wndpos += 2;
    if (wndpos == 20)
        wndpos = 2;
    wnd1 = CreateWindow(EDITBOX,
                Fname,
                (wndpos-1)*2, wndpos, 10, 40,
                NULL, wnd, EditorProc,
                SHADOW     |
                MINMAXBOX  |
                CONTROLBOX |
                VSCROLLBAR |
                HSCROLLBAR |
                MOVEABLE   |
                HASBORDER  |
                SIZEABLE   |
                MULTILINE
    );
    if (strcmp(FileName, Untitled))    {
        if ((wnd1->extension =
                malloc(strlen(FileName)+1)) != NULL)    {
            strcpy(wnd1->extension, FileName);
            LoadFile(wnd1, (int) sb.st_size);
        }
    }
    SendMessage(wnd1, SETFOCUS, TRUE, 0);
}
/* --- Load the notepad file into the editor text buffer --- */
static void LoadFile(WINDOW wnd, int tLen)
{
    char *Buf;
    FILE *fp;

    if ((Buf = malloc(tLen+1)) != NULL)    {
        if ((fp = fopen(wnd->extension, "rt")) != NULL)    {
            memset (Buf, 0, tLen+1);
            fread(Buf, tLen, 1, fp);
            SendMessage(wnd, SETTEXT, (PARAM) Buf, 0);
            fclose(fp);
        }
        free(Buf);
    }
}
/* --- print the current notepad --- */
static void PrintPad(WINDOW wnd)
{
    unsigned char *text;

    /* ---------- print the file name ---------- */
    fputs("\r\n", stdprn);
    fputs(GetTitle(wnd), stdprn);
    fputs(":\r\n\n", stdprn);

    /* ---- get the address of the editor text ----- */
    text = GetText(wnd);

    /* ------- print the notepad text --------- */
    while (*text)    {
        if (*text == '\n')
            fputc('\r', stdprn);
        fputc(*text++, stdprn);
    }

    /* ------- follow with a form feed? --------- */
    if (YesNoBox("Form Feed?"))
        fputc('\f', stdprn);
}
/* ---------- save a file to disk ------------ */
static void SaveFile(WINDOW wnd, int Saveas)
{
    FILE *fp;
    if (wnd->extension == NULL || Saveas)    {
        char FileName[64];
        if (SaveAsDialogBox(FileName))    {
            if (wnd->extension != NULL)
                free(wnd->extension);
            if ((wnd->extension =
                    malloc(strlen(FileName)+1)) != NULL)    {
                strcpy(wnd->extension, FileName);
                AddTitle(wnd, NameComponent(FileName));
                SendMessage(wnd, BORDER, 0, 0);
            }
        }
        else
            return;
    }
    if (wnd->extension != NULL)    {
        WINDOW mwnd = MomentaryMessage("Saving the file");
        if ((fp = fopen(wnd->extension, "wt")) != NULL)    {
            fwrite(GetText(wnd), strlen(GetText(wnd)), 1, fp);
            fclose(fp);
            wnd->TextChanged = FALSE;
        }
        SendMessage(mwnd, CLOSE_WINDOW, 0, 0);
    }
}
/* -------- delete a file ------------ */
static void DeleteFile(WINDOW wnd)
{
    if (wnd->extension != NULL)    {
        if (strcmp(wnd->extension, Untitled))    {
            char *fn = NameComponent(wnd->extension);
            if (fn != NULL)    {
                char msg[30];
                sprintf(msg, "Delete %s?", fn);
                if (YesNoBox(msg))    {
                    unlink(wnd->extension);
                    SendMessage(wnd, CLOSE_WINDOW, 0, 0);
                }
            }
        }
    }
}
/* ------ display the row and column in the statusbar ------ */
static void ShowPosition(WINDOW wnd)
{
    char status[30];
    sprintf(status, "Line:%4d  Column: %2d",
        wnd->CurrLine, wnd->CurrCol);
    SendMessage(GetParent(wnd), ADDSTATUS, (PARAM) status, 0);
}
/* ----- window processing module for the editboxes ----- */
static int EditorProc(WINDOW wnd,MESSAGE msg,PARAM p1,PARAM p2)
{
    int rtn;
    switch (msg)    {
        case SETFOCUS:
            rtn = DefaultWndProc(wnd, msg, p1, p2);
            if ((int)p1 == FALSE)
                SendMessage(GetParent(wnd), ADDSTATUS, 0, 0);
            else
                ShowPosition(wnd);
            return rtn;
        case KEYBOARD_CURSOR:
            rtn = DefaultWndProc(wnd, msg, p1, p2);
            ShowPosition(wnd);
            return rtn;
        case COMMAND:
            if ((int) p1 == ID_HELP)    {
                DisplayHelp(wnd, "MEMOPADDOC");
                return TRUE;
            }
            break;
        case CLOSE_WINDOW:
            if (wnd->TextChanged)    {
                char *cp = malloc(25+strlen(GetTitle(wnd)));
                SendMessage(wnd, SETFOCUS, TRUE, 0);
                if (cp != NULL)    {
                    strcpy(cp, GetTitle(wnd));
                    strcat(cp, "\nText changed. Save it?");
                    if (YesNoBox(cp))
                        SendMessage(GetParent(wnd),
                            COMMAND, ID_SAVE, 0);
                    free(cp);
                }
            }
            wndpos = 0;
            if (wnd->extension != NULL)    {
                free(wnd->extension);
                wnd->extension = NULL;
            }
            break;
        default:
            break;
    }
    return DefaultWndProc(wnd, msg, p1, p2);
}
/* -- point to the name component of a file specification -- */
static char *NameComponent(char *FileName)
{
    char *Fname;
    if ((Fname = strrchr(FileName, '\\')) == NULL)
        if ((Fname = strrchr(FileName, ':')) == NULL)
            Fname = FileName-1;
    return Fname + 1;
}




Example 1:

 WINDOW cwnd = GetFirstChild(pwnd);
 while (cwnd != NULLWND)   {
     /* -- process the child window -- */
     cwnd = GetNextChild(pwnd, cwnd);

 }


Example 2:


 typedef enum messages {
     #undef DFlatMsg
     #define DFlatMsg(m) m,
     #include "dflatmsg.h"
     MESSAGECOUNT
 } MESSAGE;




Example 3

 static char *message[] = {
     #undef DFlatMsg
     #define DFlatMsg(m) " " #m,
     #include "dflatmsg.h"
     NULL

 };


Copyright © 1991, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.