Channels ▼
RSS

C/C++

C Programming

Source Code Accompanies This Article. Download It Now.


NOV93: C PROGRAMMING

Now they tell me that we're going to have 500 TV channels. I know that it's fashionable to pretend that you don't watch (or own) a TV set, but somebody must be watching to make having 500 channels pay off. Besides, its a big opportunity for programmers. Sit back and read about the next killer vertical application.

I remember when TV came to Lorton, Virginia. It was about 1949, and my grandparents had one of the first sets in town. My four brothers and I would walk to their house after school and watch Captain Video and Howdy Doody. There were four channels then, a lot for the time, but we were within range of the Washington, D.C stations. They started broadcasting at four in the afternoon and shut down at eleven at night.

The point of this parable is that it didn't take much time to figure out what to watch. It took about ten seconds to scan the channels (and that was with no remote), getting off the couch, walking to the set to flip the dial, and walk back. Most viewers had the schedule memorized, and they didn't have to scan. Just tuned it in. Sometime in the 1960s cable came along, and there were maybe ten stations to choose from. Still no problem keeping up with the schedule; flipping channels took only a little longer. Where I live now, clearly in a culturally-deprived area, we have only 36 channels and a remote. It takes maybe a minute or two to go around the dial, especially if you have to outwait the commercials to see what's on. But back in Lorton, my nephews have almost 100 channels. It takes them much longer now to find out that there's nothing on that they want to watch. My in-laws have a dish antenna on their farm in Pennsylvania, and it takes longer than most shows last to scan all of the stations. But 500 channels? How will you know what to watch? How will you know what's on?

There is a problem, and the TV industry will need a solution. It calls out for an on-line service that keeps track of everything that is scheduled for broadcast and that stores a profile of every viewer's personal viewing preference. The profile would look like a complex database query.

Select(DiNiro BUT NOT "Raging Bull")
Select(Galloping Gourmet AND broccoli)
Select(Bob Packwood AND Germaine Greer)
Select(Mr. Ed OR Ed Meese)

Each day you would be notified of the programs that matched your profile. You could rearrange your schedule to watch, or you could tell the system to automatically tape the selected programs on your multi-deck VCR.

But some problems will persist. I'm not sure how Ms. Stevens and Ms. Erickson will be able to select only the Perry Mason reruns they haven't seen yet.

Select(Perry Mason and "defendant found guilty")

There's clearly a golden opportunity for an enterprising database programmer lurking somewhere in this idea. You have my permission to run with it.

Back to D-Flat++

This month we return to D-Flat++. I shelved the project for a half year while I worked on other things. I'll review what D-Flat++ is and what we've covered so far, to bring you up to date.

D-Flat++ is a descendent of D-Flat, which is a C function library that implements the Common User Access (CUA) user interface in a DOS, text-mode environment. I published that library in this column. It took about a year-and-a-half to cover the whole thing. When I began to rewrite D-Flat as a C++ class library (formally named "D-Flat++" and called "DF++" here for short), I decided to omit some of the features that D-Flat has, mainly because I never used them, and I further decided to publish only that part of the code that is interesting enough to warrant discussion so that the project wouldn't take so long. You can get all of the source code by downloading it or by sending for a careware version. I'll explain how that works later in this column. You can get back issues of DDJ to catch up on the discussions. The November 1992 through April 1993 issues cover everything up until now. The DDJ CD-ROM includes all of D-Flat as well as D-Flat++.

Up to this point in the DF++ project we've discussed the desktop, the application, and some control windows. A DF++ application runs from a virtual desktop that contains a screen, a keyboard, a mouse, a clock, and a speaker. The desktop and the devices are represented by classes in the library. The application is a base class from which you derive a custom application class. The application's menu is defined as a part of the derived class. Your derived application class includes member functions that execute when the user chooses menu selections.

The control windows are text boxes, edit boxes, push buttons, check boxes, list boxes, and so on. They are each implemented in classes that are derived from a Control class. All of the windows in DF++ are derived from a base DFWindow class.

Portability

In an earlier column, I discussed a portability layer that lets DF++ compile with both Borland C++ and Microsoft C++. I had hoped that it would work with those compilers and perhaps some others. I gave up on Zortech when I couldn't get reliable tech support from them on CompuServe. I have now temporarily dropped Microsoft too because the MSC/C++ compiler doesn't support templates yet. Once you start using templates, you can't do without them. When time permits, I'll port DF++ to Comeau C++ (a CFRONT port) and the new Watcom C++ compiler, both of which support templates. Until then, you need Borland C++ 3.1 or newer to compile DF++.

I'm trying to avoid the container classes and templates that come with some compilers. Not that they aren't good tools, it's just that there are no standards in place yet, and the different implementations are unlike one another and probably unlike whatever the ANSI committee comes up with. That's why you'll find a Tree template and a String class and other such things bundled into DF++. I'll try to comply when ANSI publishes a draft, that is if I'm not on a rocking chair at the Old Columnists retirement village.

Old Dog, New Trick

I learned a lesson about class design that I haven't seen in any of the advanced C++ style books yet. You know which ones I mean; they tell you how and when to use this or that language feature and when not to. It's generally understood that you should make data members private and provide access to them through a member function interface. The books all tell you that. The reasons are founded in sound object-oriented design principles. When you shield the class-using programmer from the implementation details, you protect the data. You make it easier on yourself later as well, when you want to modify the implementation. By providing a function interface and hiding the implementation, you can change the implementation without affecting the user's code.

Now, here's another convention to consider. We all know that only a class's member functions can access the private data members. If those data members have a public interface, the class's members should use it, too. Why? Because when you set about to change that implementation, you'll be changing a lot less of the implementation code if the members use the public interface.

How did I come to that conclusion? Nothing worth knowing comes easy. DF++ has a base DFWindow class that handles all of the operations that are common to all windows. All of the other window classes are derived from DFWindow. One of its functions is to maintain the parent/child/sibling relationship among windows. Before templates, I built the list head, tail, and linked-list pointers into the DFWindow class itself. Not long ago, I decided to use a Family class that I had designed based on the LinkedList class that I discussed a few months ago. To my dismay, I found that a whole lot of DFWindow member functions use those embedded pointers. There was a very nice interface there for non-member functions to use, but the member functions didn't use it. My work would have been a lot easier if I had used prudent design techniques at the outset. (I'm always telling myself that.)

Resources and What C++ Left Out

D-Flat++ uses resources similar to the GUIs that it emulates. Resources in this context are menus and dialog boxes. I discussed menus in April. The menubar is defined as an instance of a MenuBar class with a list of MenuBarItem objects, each one of which has a PopDown object associated with it. Each PopDown object is defined by a list of MenuSelection objects. This is similar to the arrays of structures that D-Flat uses, except in this case we use class objects, and in this case we--sadly--have no resource language.

Windows programmers are familiar with the Resource Compiler program and resource language that defines menus and dialog boxes. I implemented the D-Flat resource language with C preprocessor macros and did a reasonable job of emulating the syntax of the Windows languages. No matter how I try, however, I haven't yet figured an elegant way to do the same thing with DF++. It seems that they left something out when they designed C++. You can't tack initializers onto the declaration of arrays of class objects if the classes are more complex than a simple C structure, which means that you can't use such initializers to declare a default dimension for the array. That limitation prevents me from using the preprocessor to translate resource statements into array declarations the same way I did with C. You gain something, you lose something.

Dialog Boxes

I discussed D-Flat++ control classes in February and March. DF++ dialog boxes are classes that you derive from a Dialog class and embed control classes in them. To illustrate the technique, and to have some commonly used dialog boxes, DF++ includes message and error windows, a yes/no selection window, and file open and save-as dialog boxes. There are five control classes derived from the PushButton class: OK and Cancel buttons, Yes and No buttons, and a Help button. We'll look at this design from the top down by examining the FileOpen and SaveAs dialog boxes. Listing One, page 141, is fileopen.h, the source file that defines the FileOpen and SaveAs dialog box classes. FileOpen is derived from the Dialog class, and SaveAs is derived from FileOpen. We'll discuss the former.

The FileOpen class contains several class objects, each of which is a control. The Label objects are static text that the dialog box displays and that the user cannot change or tab to. The program may change them, but the user cannot. The FileOpen dialog box uses several Labels to identify the other controls. D-Flat++ associates a Label object with a control if the Label object immediately precedes the control. If the Label has a shortcut key defined, the user can tab directly to the control by pressing Alt and the shortcut key--just like Windows. Observe that the control definitions have no initializers to give them any details. You cannot express initializing values inside the class definition. The dialog box's constructor do that.

Drives, Subdirectories, and Paths

Three of the control classes used in the FileOpen dialog box class are derived from the ListBox control class. They are the FileNameListBox control, the DirectoryListBox control, and the DriveListBox control. A fourth class, the PathNameLabel control, is derived from the TextBox class. These control classes are defined in Listing Two, page 140, directry.h. Their purposes are to display the current path and lists of the files, directories, and drives. Listing Three, page 140, is directry.cpp, which contains the member functions for the disk and directory control classes. The lists are initialized by their constructors. All but the DriveListBox class have member functions that allow a using dialog box to change their lists. For example, when the user changes drives or subdirectories, the dialog box needs to update the lists of files and subdirectories.

There is some dark logic in the DriveListBox constructor to determine whether the drive is a RAM disk or a network drive or to see if the B: drive is remapped to A: as happens when a computer has only one diskette drive. The logic involves calls to the DOS IOCTL function with some undocumented protocols. (Just my small contribution to Andrew Schulman's "Undocumented Corner.")

Listing Four, page 140, is fileopen.cpp, which contains the member functions for the FileOpen and SaveAs classes. The declarator for the class constructor is where the program defines the properties of the dialog box and its controls by providing parameters for their constructors. In this case, the Dialog class is constructed with the "File Open" title, a height of 19, and a width of 57. The Label objects are provided with text values to display and x/y screen position relative to the dialog box parent window. The tilde character (~) in the text identifies the label's shortcut key. Other controls have positions and other necessary initializers.

Several of the FileOpen member functions are overrides of virtual functions in the DFWindow and Dialog classes. The ControlSelected and ControlChosen functions are called when a control is selected or chosen. For example, when the user moves the selector bar on a list box, this action is called "selecting" the item. When the user double-clicks or presses Enter on an item, this is called "choosing" the item. The control window reports these actions to its parent by calling virtual functions. This notification allows the parent, a dialog box in this case, to take action based on the user's actions. In the FileOpen class, for example, the user's selection of a file name from the list causes the dialog box to copy that name into the file name edit box.

There are OKFunction, CancelFunction, and HelpFunction virtual functions that are called when the user chooses the related pushbutton. The first two are called as well when the user presses Enter and Esc, and the third one is called when the user presses F1. These functions allow the dialog box to take whatever action is appropriate. If the derived dialog box class does not override them, the Dialog class takes default actions. For the first two, it closes the dialog box window and returns to the program that built it. HelpFunction, after I have it completed, will display help about dialog boxes in general if the derived dialog box does not override the function.

The derived dialog class may override two more functions. These are the EnterFocus function, which is called when a control is about to get the focus, and the LeaveFocus function, which is called when a control has lost the focus. These functions allow the dialog box to take appropriate action on the user's entries as they occur.

The Dialog Class

Listing Five, page 143, is dialog.h, which defines the base Dialog class. The class definition is fairly simple. It includes a few data members and the primitive functions common to all dialog boxes. The constructors are all protected, so you cannot instantiate an object of the class. You must derive a class such as the FileOpen class discussed above and instantiate it instead. Listing Six, page 143, is dialog.cpp, which contains the member functions. The TestShortCut function associates shortcut keys on labels to their immediately following control class objects. The Execute function runs the dialog box after it is constructed. I couldn't simply call that function from the constructor because that would preclude any derived dialog box classes from overriding it or any other member functions. The Execute function captures the focus for the dialog box and sets the focus to the first enabled control in the dialog box. Then it stays in a loop, calling DispatchEvents for as long as the dialog box is active.

Using a Dialog Box

A program displays the FileOpen dialog box on the screen and executes it by declaring an instance of the class and calling its Execute member function as shown here:

FileOpen fo;
fo.Execute();

The Execute function gives the user's focus to the dialog box and does not return to the calling program until the user chooses the OK or Cancel button. The program can test the result by calling the OKExit member function as shown here:

if (fo.OKExit())     {
String fname = String(fo.FileName());
// ....
}

The FileName member function returns the chosen filename. Although the dialog box is closed and erased from the screen, the class object exists until it goes out of scope, so the program retrieve data values from the controls as the user changed them.

These procedures are the same for all modal dialog boxes. I haven't worked out those for modeless dialog boxes yet, but that feature should be in place by the time you read this.

How to Get the Source Code

D-Flat++ is still preliminary but far closer to a working version than the first one. I'm writing this column in August and will soon upload Version 2. The first version was incomplete, but there was enough of an implementation to give you an idea of how DF++ works and how it differs from D-Flat. The second version has enough functionality to build an application, although there are more features to come. Later versions should be released by the time you read this. The C D-Flat function library is still available, too. You can download DF and DF++ from the CompuServe DDJ Forum or from M&T Online. You can also get them by sending a stamped, self-addressed diskette mailer and a formatted diskette to me at Dr. Dobb's Journal, 411 Borel Avenue, San Mateo, California 94402. The software is free, but if you wish, include a dollar for my "careware" charity, the Brevard County Food Bank.



_C PROGRAMMING COLUMN_
by Al Stevens

[LISTING ONE]

// --------- fileopen.h

#ifndef FILEOPEN_H
#define FILEOPEN_H

#include "dflatpp.h"
#include "directry.h"

// ------------ File Open dialog box
class FileOpen : public Dialog    {
    // -----File Open Dialog Box Controls:
protected:
    // ----- file name editbox
    Label filelabel;
    EditBox filename;
    // ----- drive:path display
    PathNameLabel dirlabel;
    // ----- files list box
    Label fileslabel;
    FileNameListBox files;
    // ----- directories list box
    Label dirslabel;
    DirectoryListBox dirs;
    // ----- drives list box
    Label disklabel;
    DriveListBox disks;
    // ----- command buttons
    OKButton ok;
    CancelButton cancel;
    HelpButton help;
    // ------ file open data members
    String filespec;
    // ------ private member functions
    void SelectFileName();
    void ShowLists();
    // --- functions inherited from DFWindow
    virtual void ControlSelected(DFWindow *Wnd);
    virtual void ControlChosen(DFWindow *Wnd);
    virtual void EnterFocus(DFWindow *Wnd);
    virtual void OKFunction();
public:
    FileOpen(char *spec = "*.*", char *ttl = "File Open");
    const String& FileName() { return filespec; }
};
class SaveAs : public FileOpen    {
    virtual void OKFunction();
public:
    SaveAs(char *spec = "", char *ttl = "Save As")
        : FileOpen(spec, ttl) { }
};
#endif



[LISTING TWO]
<a name="0345_0010">

// ---------- directry.h

#ifndef DRIVE_H
#define DRIVE_H

#include <dir.h>
#include <dos.h>
#include "listbox.h"
#include "label.h"

class PathNameLabel : public Label {
    char *CurrentPath();
public:
    PathNameLabel(int x, int y, int wd) :
        Label(CurrentPath(), x, y, wd) {}
    void FillLabel();
};
class DriveListBox : public ListBox    {
    unsigned currdrive;
public:
    DriveListBox(int lf, int tp);
};
class DirectoryListBox : public ListBox    {
public:
    DirectoryListBox(int lf, int tp);
    void FillList();
};
class FileNameListBox : public ListBox    {
public:
    FileNameListBox(char *filespec, int lf, int tp);
    void FillList(char *filespec);
};
#endif


<a name="0345_0011"><a name="0345_0012">
[LISTING THREE]
<a name="0345_0012">

// ---------- directry.cpp

#include <direct.h>
#include "directry.h"

char *PathNameLabel::CurrentPath()
{
    static char path[129];
    _getdcwd(0, path, 129);
    return path;
}
void PathNameLabel::FillLabel()
{
    SetText(CurrentPath());
}
DriveListBox::DriveListBox(int lf, int tp) : ListBox(lf, tp, 10, 10, 0)
{
    SetAttribute(BORDER);
    currdrive = getdisk();
    union REGS regs;
    for (unsigned int dr = 0; dr < 26; dr++)    {
        setdisk(dr);
        if (getdisk() == dr)    {
            // ----- test for remapped B drive
            if (dr == 1)    {
                regs.x.ax = 0x440e; // IOCTL func 14
                regs.h.bl = dr+1;
                int86(0x21, &regs, &regs);
                if (regs.h.al != 0)
                    continue;
            }
            String drname(" :");
            drname[0] = dr+'A';
            // ---- test for network or RAM disk
            regs.x.ax = 0x4409;     // IOCTL func 9
            regs.h.bl = dr+1;
            int86(0x21, &regs, &regs);
            if (!regs.x.cflag)    {
                if (regs.x.dx & 0x1000)
                    drname += " (Net)";
                else if (regs.x.dx == 0x0800)
                    drname += " (RAM)";
            }
            AddText(drname);
        }
    }
    setdisk(currdrive);
    SetScrollBars();
}
DirectoryListBox::DirectoryListBox(int lf, int tp) : ListBox(lf, tp, 10, 13, 0)
{
    SetAttribute(BORDER);
    FillList();
}
void DirectoryListBox::FillList()
{
    ClearText();
    int ax;
    struct ffblk ff;
    ax = findfirst("*.*", &ff, FA_DIREC);
    while (ax == 0)    {
        if ((ff.ff_attrib & FA_DIREC) != 0)    {
            if (strcmp(ff.ff_name, "."))    {
                String fname("[");
                fname += ff.ff_name;
                fname += "]";
                AddText(fname);
            }
        }
        ax = findnext(&ff);
    }
    SetScrollBars();
}
FileNameListBox::FileNameListBox(char *filespec,int lf,int tp)
                        : ListBox(lf, tp, 10, 14, 0)
{
    SetAttribute(BORDER);
    FillList(filespec);
}
void FileNameListBox::FillList(char *filespec)
{
    ClearText();
    int ax;
    struct ffblk ff;
    ax = findfirst(*filespec ? filespec : "*.*", &ff, 0);
    while (ax == 0)    {
        AddText(ff.ff_name);
        ax = findnext(&ff);
    }
    SetScrollBars();
}


<a name="0345_0013"><a name="0345_0014">
[LISTING FOUR]
<a name="0345_0014">

// ---------- fileopen.cpp

#include <io.h>
#include <dir.h>
#include "fileopen.h"
#include "notice.h"

// ----------------- File Open Dialog Box
FileOpen::FileOpen(char *spec, char *ttl) :
        Dialog(ttl, 19, 57),
        // ----- file name editbox
        filelabel ("~Filename:", 3, 2),
        filename  (13, 2, 1, 40),
        // ----- drive:path display
        dirlabel  (3, 4, 50),
        // ----- files list box
        fileslabel("F~iles:", 3, 6),
        files     (spec, 3, 7),
        // ----- directories list box
        dirslabel ("~Directories:", 19, 6),
        dirs      (19, 7),
        // ----- drives list box
        disklabel ("Dri~ves:", 34, 6),
        disks     (34, 7),
        // ----- command buttons
        ok        (46, 8),
        cancel    (46,11),
        help      (46,14),
        // ------ file open data members
        filespec(spec)
{
    filename.AddText(filespec);
}
// --- Get selected filename: files listbox->filename editbox
void FileOpen::SelectFileName()
{
    int sel = files.Selection();
    if (sel != -1)    {
        String fname = files.ExtractTextLine(sel);
        filename.SetText(fname);
        filename.Paint();
    }
}
// ---- called when user "selects" a control
//      e.g. changes the selection on a listbox
void FileOpen::ControlSelected(DFWindow *Wnd)
{
    if (Wnd == (DFWindow *) &files)
        // --- user selected a filename from list
        SelectFileName();
    else if (Wnd == (DFWindow *) &dirs ||
            Wnd == (DFWindow *) &disks)    {
        // --- user is selecting a different drive or directory
        filename.SetText(filespec);
        filename.Paint();
    }
}
// ---- called when user "chooses" a control
//      e.g. chooses the current selection on a listbox
void FileOpen::ControlChosen(DFWindow *Wnd)
{
    if (Wnd == (DFWindow *) &files)
        // --- user chose a filename from filename list
        OKFunction();
    else if (Wnd == (DFWindow *) &dirs)    {
        // --- user chose a directory from directory list
        int dr = dirs.Selection();
        String dir = dirs.ExtractTextLine(dr);
        int len = dir.Strlen();
        String direc = dir.mid(len-2,1);
        chdir(direc);
        ShowLists();
    }
    else if (Wnd == (DFWindow *) &disks)    {
        // --- user chose a drive from drive list
        int dr = disks.Selection();
        String drive = disks.ExtractTextLine(dr);
        setdisk(drive[0] - 'A');
        ShowLists();
    }
}
// ---- called when user chooses OK command
void FileOpen::OKFunction()
{
    String fname = filename.ExtractTextLine(0);
    if (access(fname, 0) == 0)    {
        filespec = fname;
        Dialog::OKFunction();
    }
    else if (fname.FindChar('*') != -1 ||
            fname.FindChar('?') != -1)    {
        filespec = fname;
        ShowLists();
    }
    else
        // ---- No file as specified
        ErrorMessage("File does not exist");
}
// ------ refresh the current directory display and the directories and files
//        list after user changes filespec, drive, or directory
void FileOpen::ShowLists()
{
    dirlabel.FillLabel();
    dirlabel.Show();
    dirs.FillList();
    dirs.Show();
    files.FillList(filespec);
    files.Show();
}
// ------- called just before a control gets the focus
void FileOpen::EnterFocus(DFWindow *Wnd)
{
    if (Wnd == (DFWindow *) &files)
        // --- The file name list box is getting the focus
        SelectFileName();
}
// ---- called when user chooses OK command
void SaveAs::OKFunction()
{
    String fname = filename.ExtractTextLine(0);
    if (access(fname, 0) != 0)    {
        // ---- chosen file does not exist
        if (fname.FindChar('*') != -1 ||
            fname.FindChar('?') != -1)    {
            // --- wild cards
            filespec = fname;
            ShowLists();
        }
        else    {
            filespec = fname;
            Dialog::OKFunction();
        }
    }
    else    {
        // ---- file exists
        String msg = fname + " already exists. Replace?";
        if (YesNo(msg))    {
            filespec = fname;
            Dialog::OKFunction();
        }
    }
}


<a name="0345_0015"><a name="0345_0016">
[LISTING FIVE]
<a name="0345_0016">

// -------- dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include "dfwindow.h"
#include "desktop.h"
#include "control.h"

class Control;
class Dialog : public DFWindow    {
    virtual void SetColors();
    Bool isRunning;
    Bool okexit;
    void OpenWindow();
    friend Control;
    void TestShortcut(int key);
protected:
    Dialog(char *ttl, int lf, int tp, int ht, int wd,
            DFWindow *par = (DFWindow *)desktop.ApplWnd())
                : DFWindow(ttl, lf, tp, ht, wd, par)
            { OpenWindow(); }
    Dialog(char *ttl, int ht, int wd,
            DFWindow *par = (DFWindow *)desktop.ApplWnd())
                : DFWindow(ttl, ht, wd, par)
            { OpenWindow(); }
    virtual ~Dialog() {}
    virtual void CloseWindow();
    virtual void Keyboard(int key);
    virtual void OKFunction();
    virtual void CancelFunction();
    virtual void HelpFunction();
public:
    virtual void Execute();
    Bool OKExit() { return okexit; }
};
#endif




<a name="0345_0017"><a name="0345_0018">
[LISTING SIX]
<a name="0345_0018">

// ------------- dialog.cpp

#include <ctype.h>
#include "dialog.h"
#include "desktop.h"

Dialog *ThisDialog;

// ----------- common constructor code
void Dialog::OpenWindow()
{
    ThisDialog = this;
    windowtype = DialogWindow;
    DblBorder = False;
    SetAttribute(BORDER | SAVESELF | CONTROLBOX |
        MOVEABLE | SHADOW);
    isRunning = False;
    okexit = False;
}
void Dialog::CloseWindow()
{
    ReleaseFocus();
    DFWindow::CloseWindow();
    isRunning = False;
}
// -------- set the fg/bg colors for the window
void Dialog::SetColors()
{
    colors.fg =
    colors.sfg =
    colors.ffg =
    colors.hfg = LIGHTGRAY;
    colors.bg =
    colors.sbg =
    colors.fbg =
    colors.hbg = BLUE;
}
void Dialog::Keyboard(int key)
{
    switch (key)    {
        case ESC:
            CancelFunction();
            break;
        case '\r':
            OKFunction();
            break;
        case ALT_F4:
            CloseWindow();
            break;
        case ' ':
            if ((desktop.keyboard().GetShift() & ALTKEY) == 0)
                break;
            // ---- fall through
        case ALT_F6:
            DFWindow::Keyboard(key);
            break;
        default:
            if ((desktop.keyboard().GetShift() & ALTKEY) != 0)
                TestShortcut(key);
            break;
    }
}
void Dialog::TestShortcut(int key)
{
    key = desktop.keyboard().AltConvert(key);
    key = tolower(key);
    Control *Ctl = (Control *)First();
    while (Ctl != 0)    {
        if (key == Ctl->Shortcut())    {
            Ctl->ShortcutSelect();
            break;
        }
        Ctl = (Control *) Ctl->Next();
    }
}
void Dialog::Execute()
{
    ThisDialog = 0;
    CaptureFocus();
    Control *Ctl = (Control *) First();
    while (Ctl != 0)    {
        if (Ctl->isEnabled())    {
            Ctl->SetFocus();
            break;
        }
        Ctl = (Control *) Ctl->Next();
    }
    // ---- modal dialog box
    isRunning = True;
    while (isRunning)
        desktop.DispatchEvents();
}
void Dialog::OKFunction()
{
    okexit = True;
    CloseWindow();
}
void Dialog::CancelFunction()
{
    CloseWindow();
}
void Dialog::HelpFunction()
{
}





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

Video