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


MAY95: C PROGRAMMING

DFWrap: The D-Flat C++ Wrapper Classes

This month's column tolls the final bell for D-Flat subjects. Several years ago I launched the D-Flat project, a C function library that implements in DOS text mode the IBM Common User Access (CUA) interface, the standard under which Windows 3.1 and OS/2 operate. At the time, there were few such libraries, and the project demonstrated not only the construction of a user-interface library, but also the underlying message-based, event-driven architecture.

Later, I developed D-Flat++, a C++ version with limited features. D-Flat++ is not a C++ wrapper, but a rewrite using features of the C++ language to implement the interface. It lacks several D-Flat features that, at the time, I thought I would never need. Consequently, every time I started another DOS, text-mode project that needed menus, dialog boxes, and the mouse, I wrote it in C and used D-Flat rather than D-Flat++. A case in point is Quincy, the C interpreter.

A couple of months ago, I got my first Internet account on ddj.com. The host provides a UNIX text-mode interface, and I decided to gradually shift my e-mail from CompuServe to that account. The Internet system uses the standard UNIX mail program, a text-mode operation that you access from a PC with a communications program. My thoughts turned to a "C Programming" column project from 1989, where I implemented Smallcom, a DOS communications program with a C-like script language. I resurrected Smallcom, ran tests to see if it would compile with contemporary compilers, installed an ancient copy of Turbo C 2.0 when it would not, and wrote a script to get my Internet mail. The program works, but the interface is not as intuitive as it used to be. I decided to rewrite the program and once again chose D-Flat as the operating platform and C as the language.

The development of the new program, called "IMail," proceeded normally except that the C++ paradigm kept nagging at me from the background. Every time I hit a design or coding decision that involved anything complex, I would be reminded that the job would be ever so much easier in C++. Changing to D-Flat++ was not the answer. I wanted several D-Flat features that I left out of D-Flat++. Ah, the precious wisdom of unerring hindsight. As the program neared completion, I revealed to two programmer friends that I had actually started this recent project in C. They shook their heads and looked solemnly and knowingly, first at one another and then at the ground. They commented in unison and in a low whisper, "I couldn't live like that."

Their remarks and their shared pity for me and my plight made me go away and think. They are right. I am wrong. This masochistic regression must be put in check. To action.

The next day saw a change of course. I put a C++ wrapper around D-Flat and rewrote IMail in C++ (not all in the same day, mind you). The C++ wrapper, which I called "DFWrap" for want of a better name, is the subject of this column. The project called for some minor modifications to D-Flat to support the wrapper, and I used that opportunity to install other small enhancements and bug fixes. DFWrap requires the release of one more, and, I hope, final version of D-Flat.

The wrapper has several advantages over D-Flat++. First, D-Flat uses a technique for describing menus and dialog boxes that resembles the resource languages of Windows programs. Instead of a resource compiler, D-Flat uses preprocessor macros to convert the resource language into initialized arrays of C structures. D-Flat++ uses class definition and inheritance as the resource language. I discovered early on that the earlier technique is more intuitive, at least to my eye, even though the approach uses the dreaded, scorned, and hated preprocessor. I felt better about doing it when I saw that Microsoft used the same technique to implement message maps in Visual C++ windows programs. A second advantage is that D-Flat supports its own version of the multiple-document interface. Third, because I have been using D-Flat exclusively for several years, it is better tested, and I trust it more. Fourth, a reader named Ed Diener ported D-Flat to the 32-bit, protected-mode Watcom compiler, a preferred platform for DOS programs. Finally, I weighed the effort required to write the wrapper against that required to incorporate all those features into D-Flat++, and the wrapper effort won hands down.

DFWrap Applications

You write a DFWrap application by designing your menus and dialog boxes the old D-Flat way and deriving an applications class from the dfApplication class; see Example 1(a).

The MAPDEF statement defines the presence of the application's message map. Other classes derived from other DFWrap base classes can have their own message maps. The message map itself is defined in the CPP source-code file for the class, as in Example 1(b).

It will be obvious to Windows programmers that the MSGMAP implementation resembles that of MFC. The MSG entries may be COMMAND message-identification codes or message codes themselves such as CLOSE_WINDOW (as the example shows), LEFT_BUTTON, KEYBOARD, and so on. The function names are member functions of the class. Message-connected functions are defined in Example 1(c).

The message functions return true if they fully process the message and want to prevent it from being passed up the window hierarchy; otherwise they return false. A message function can cause the message to be processed by higher windows by calling the DispatchBaseDFMessage function and passing the message identification as a parameter.

The application's main function declares an object of the derived dfApplication class and calls its dfApplication::Run() member function; see Example 2(a). The application defines dialog boxes by deriving classes from the dfDialog class and providing message-map entries for the dialog's controls, as in Example 2(b).

The definition of the dialog box itself is the same as in D-Flat, with the preprocessor resource language. The definition is accompanied by the message map and member functions that process the dialog box; see Example 3(a). The application launches modal and modeless dialog boxes as in Example 3(b).

DFWrap Source Code

Listing One is dfwrap.h, the header file that an application includes to use the wrapper classes. It defines the ubiquitous bool type and includes dflat.h as an extern "C" header file. Then it includes the other header files that declare the wrapper classes.

Listing Two is dfrect.h, a utility window rectangle class that provides an object-oriented interface to the rectangles that D-Flat manages.

Listings Three and Four are dfwnd.h and dfwnd.cpp, which declare and define the dfWindow class, the highest class in the DFWrap hierarchy. The header file defines the MAPDEF, MSGMAP, MSG, and ENDMAP macros, which expand into inline and member functions that implement the message map. This expansion causes the named member functions of the derived class to be called when the associated messages are sent by the system. The header file declares the base dfWindow class, which is little more than a wrapper around D-Flat window processing. The notable exception is the static mf_WndProc function, which intercepts and dispatches messages to the derived window. When the application instantiates an object derived from dfWindow, its constructor calls the D-Flat CreateWindow function, passing the mf_WndProc function address as the window-processing module. That function receives messages sent to the window and calls the class's DispatchDFMessage member function, which is declared by the MAPDEF macro and defined by the MSGMAP, MSG, and ENDMAP macros. That function tests the message in a switch statement with cases generated from the MSG macros. Those cases call the derived-class member function assigned to the matching message.

The dfApplication and dfDialog Classes

Listings Five and Six are dfappl.h and dfappl.cpp, respectively, the source-code files that declare and define the dfApplication class. This is a small wrapper class that associates the menu with the application and launches the application. Similarly, Listings Seven and Eight are dfdial.h and dfdial .cpp, respectively. They put a wrapper around the D-Flat dialog-box mechanism. These two classes encapsulate as much of D-Flat as I needed for the mail application. If I use DFWrap for other applications, it might become necessary to add to the interface.

Common Dialog Boxes

The wrapper provides access to the common D-Flat dialog boxes--File Open, File Save, Message Box, Error Box, YesNo Box, and so on--through the same function calls that D-Flat C programs use. There seemed to be no point in changing that interface.

The IMail Program

You can download D-Flat v20, DFWrap, and the IMail program from CompuServe, ftp.mv.com, and DDJ Online. The IMail program is a far more comprehensive example of the use of the wrapper classes than I can include in the column of a single month. It uses a multiple-document interface to display the lists of and text of incoming and outgoing mail messages. There are several modal and modeless dialog boxes that implement logon options, communications options, an address book, and a file cabinet for storing messages. The program also includes modem and serial-port logic encapsulated into C++ classes.

I don't know if I will use D-Flat much more. The day of the DOS, text-mode application is bygone, I suspect. Now if someone would only write a graphics version of D-Flat that provides the look and feel of a Windows 95 application_.

Example 1: (a) Deriving an applications class; (b) message map; (c) message-connected functions.

(a)
class MailAppl : public dfApplication   {
    bool OnFile();     // menu command
    bool OnClose();    //   "
protected:
    MAPDEF(dfApplication)
public:
    MailAppl();
    virtual ~MailAppl();
};

(b)
MSGMAP(MailAppl)
    MSG(ID_FILE,      OnAbout)
    MSG(CLOSE_WINDOW, OnClose)
ENDMAP

(c)
bool MailAppl::OnFile()
{
    // ...
    return false;
}

Example 2: (a) Declaring an object of the derived dfApplication class; (b) deriving classes from the dfDialog class.

(a)
MailAppl *mailappl;
int main()
{
    mailappl = new MailAppl;
    mailappl->Run();
    delete mailappl;
    return 0;
}

(b)
extern DBOX AddrBookDB;
class AddrBook : public dfDialog    {
    bool OnOK();
protected:
    MAPDEF(dfDialog)
public:
    AddrBook() : dfDialog(AddrBookDB)
        { }
};

Example 3: (a) Definition of the dialog box; (b) launching modal and modeless dialog boxes.

(a)
DIALOGBOX( AddrBookDB )
    DB_TITLE( "Address Book", -1, -1, 15, 60 )
    CONTROL(TEXT, "~Addresses:",  1, 0, 1,10, ID_ADDRESS)
    CONTROL(LISTBOX,  NULL,       1, 1,12,45, ID_ADDRESS)
    CONTROL(BUTTON, " ~Select ", 48, 1, 1, 8, ID_OK)
ENDDB
MSGMAP(AddrBook)
    MSG(ID_ADDRESS, OnAddress)
    MSG(ID_OK,      OnOK)
ENDMAP
bool AddrBook::OnAddress()
{
    // ...
    return true;
}
bool AddrBook::OnOK()
{
    // ...
    return new;
}

(b)
AddrBook *p_addrbook = new AddrBook;
p_addrbook->doModal();  // or doModeless()
delete p_addrbook;

Listing One


// ------ dfwrap.h

#ifndef DFWRAP_H
#define DFWRAP_H

typedef int bool;
const int true = 1;
const int false = 0;

#include <assert.h>

extern "C" {
#include "dflat.h"
}
#include "dfrect.h"
#include "dfwnd.h"
#include "dfappl.h"
#include "dfdial.h"

#endif


Listing Two


// -------- dfrect.h
#ifndef DFRECT_H
#define DFRECT_H

class Rect  {
    RECT m_rect;
public:
    Rect(RECT rc) : m_rect(rc)
        { /* ... */ }
    Rect(int x=0, int y=0, int ht=1, int wd=1)
    {
        m_rect.lf = x;
        m_rect.tp = y;
        m_rect.rt = x+wd-1;
        m_rect.bt = y+ht-1;
    }
    operator RECT()
        { return m_rect; }

    int Left() const
        { return m_rect.lf; }
    int Top() const
        { return m_rect.tp; }
    int Height() const
        { return m_rect.bt - m_rect.tp + 1; }
    int Width() const
        { return m_rect.rt - m_rect.lf + 1; }
    int Right() const
        { return m_rect.rt; }
    int Bottom() const
        { return m_rect.bt; }
    int Area() const
        { return Height() * Width(); }
};
#endif


Listing Three


// ------- dfwnd.h
#ifndef DFWND_H
#define DFWND_H

#include <cstring.h>

// -------------------------------------------------------------
#define MAPDEF(base) \
virtual bool DispatchDFMessage(MESSAGE msg); \
bool DispatchBaseDFMessage(MESSAGE msg)      \
{                                            \
    MESSAGE sm=m_msg;PARAM s1=p1,s2=p2;      \
    bool rtn = base::DispatchDFMessage(msg); \
    if (rtn&&msg==CLOSE_WINDOW)m_Wnd=0;      \
    m_msg=sm;p1=s1;p2=s2;                    \
    return rtn;                              \
}
// -------------------------------------------------------------
#define MSGMAP(ap) \
bool ap::DispatchDFMessage(MESSAGE msg) \
{MESSAGE sm=m_msg;PARAM s1=p1,s2=p2;bool rtn=true;switch(msg){
// -------------------------------------------------------------
#define MSG(id,fn) case id:rtn=fn();break;
// -------------------------------------------------------------
#define ENDMAP  default:break;}         \
                m_msg=sm;p1=s1;p2=s2;   \
                return rtn?DispatchBaseDFMessage(msg):false; \
}
// -------------------------------------------------------------
class dfWindow {
protected:
    static dfWindow *mp_cwindow;
    WINDOW m_Wnd;
    MESSAGE m_msg;
    PARAM p1, p2;
    dfWindow();
    dfWindow(CLASS cl, const string& ttl, const Rect& rc = Rect(0,0,-1,-1),
            void *ext = 0, int attrib = 0);
    void CommonConstructor();
    virtual ~dfWindow()
        { }
    static bool mf_WndProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2);
    virtual bool DispatchDFMessage(MESSAGE)
        { return DefaultWndProc(m_Wnd, m_msg, p1, p2); }
public:
    void Show()
        { SendMessage(SHOW_WINDOW);}
    void Hide()
        { SendMessage(HIDE_WINDOW); }
    void CloseWindow()
        { SendMessage(CLOSE_WINDOW); }
    void Paint()
        { SendMessage(PAINT); }
    void Move(int x, int y)
        { SendMessage(MOVE, x, y); }
    void Size(int h, int w)
        { SendMessage(SIZE,
            GetTop(m_Wnd)+h-1, GetRight(m_Wnd)+w-1); }
    bool isAttributeSet(int attrib)
        { return TestAttribute(m_Wnd, attrib); }
    void SetAttribute(int attrib)
        { AddAttribute(m_Wnd, attrib); }
    void ResetAttribute(int attrib)
        { ClearAttribute(m_Wnd, attrib); }
    int SendMessage(MESSAGE msg, PARAM p1 = 0, PARAM p2 = 0)
        { return ::SendMessage(m_Wnd, msg, p1, p2); }
    void PostMessage(MESSAGE msg, PARAM p1 = 0, PARAM p2 = 0)
        { ::PostMessage(m_Wnd, msg, p1, p2); }
    int SendCommand(PARAM cmd, PARAM p2 = 0)
        { return ::SendMessage(m_Wnd, COMMAND, cmd, p2); }
    void PostCommand(PARAM cmd, PARAM p2 = 0)
        { ::PostMessage(m_Wnd, COMMAND, cmd, p2); }
    int Top()
        { return GetTop(m_Wnd); }
    int Bottom()
        { return GetBottom(m_Wnd); }
    int Left()
        { return GetLeft(m_Wnd); }
    int Right()
        { return GetRight(m_Wnd); }
    int Height()
        { return WindowHeight(m_Wnd); }
    int Width()
        { return WindowWidth(m_Wnd); }
    int ClientTop()
        { return GetClientTop(m_Wnd); }
    int ClientBottom()
        { return GetClientBottom(m_Wnd); }
    int ClientLeft()
        { return GetClientLeft(m_Wnd); }
    int ClientRight()
        { return GetClientRight(m_Wnd); }
    int ClHeight()
        { return ClientHeight(m_Wnd); }
    int ClWidth()
        { return ClientWidth(m_Wnd); }
#ifdef INCLUDE_MINIMIZE
    void Minimize()
        { SendMessage(MINIMIZE); }
    bool isMinimized()
        { return m_Wnd->condition == ISMINIMIZED; }
#endif
#ifdef INCLUDE_MAXIMIZE
    void Maximize()
        { SendMessage(MAXIMIZE); }
    bool isMaximized()
        { return m_Wnd->condition == ISMAXIMIZED; }
#endif
#ifdef INCLUDE_RESTORE
    void Restore()
        { SendMessage(RESTORE); }
    bool isRestored()
        { return m_Wnd->condition == ISRESTORED; }
    Rect RestoredRect()
        { return m_Wnd->RestoredRC; }
    void SetRestoredRect(Rect rc)
        { m_Wnd->RestoredRC = rc; }
#endif
    Rect WndRectangle()
        { return WindowRect(m_Wnd); }
    void SetWndRectangle(Rect rc);
    void SetWndCondition(Condition cnd)
        { m_Wnd->condition = cnd; }
    Rect ClientRectangle()
        { return Rect(ClientLeft(), ClientTop(), ClHeight(), ClWidth()); }
    void SetForeground(char fg)
        { WndForeground(m_Wnd) = fg; }
    void SetBackground(char bg)
        { WndBackground(m_Wnd) = bg; }
    void ShowCursor()
        { ::SendMessage(0, SHOW_CURSOR, 0, 0); }
    void HideCursor()
        { ::SendMessage(0, HIDE_CURSOR, 0, 0); }
    void SetCursor()
        { cursor(ClientLeft()+Column(), ClientTop()+Row()); }
    void CursorHome()
        { m_Wnd->CurrCol = 0; SetCursor(); }
    void CursorUp(int ct = 1);
    void CursorDown(int ct = 1);
    void CursorRight(int ct = 1);
    void CursorLeft(int ct = 1);
    void WriteChar(int ch);
    int Row()
        { return m_Wnd->WndRow; }
    int Column()
        { return m_Wnd->CurrCol; }
    void SetRow(int y)
        { m_Wnd->WndRow = y; }
    void SetColumn(int x)
        { m_Wnd->CurrCol = x; }
    void PositionCursor(int x, int y);
    void ScrollUp()
        { scroll_window(m_Wnd, ClientRectangle(), TRUE); }
    void ClearScreen();
    void ClearToEOL();
    void SetFocus()
        { SendMessage(SETFOCUS, true); }
    void SetRestoredAttrib(int attr)
        { m_Wnd->restored_attrib = attr; }
    int GetRestoredAttrib() const
        { return m_Wnd->restored_attrib; }
};
#endif


Listing Four


// ---------- dfwnd.cpp
#include "dfwrap.h"

dfWindow *dfWindow::mp_cwindow;

void dfWindow::CommonConstructor()
{
    m_Wnd = 0;
    p1 = 0;
    p2 = 0;
}
dfWindow::dfWindow()
{
    CommonConstructor();
}
dfWindow::dfWindow(CLASS cl, const string& ttl, const Rect& rc,
        void *ext, int attrib)
{
    CommonConstructor();
    if (cl == APPLICATION)
        init_messages();
    mp_cwindow = this;
    CreateWindow(cl,ttl.c_str(),
                rc.Left(),rc.Top(),rc.Height(),rc.Width(),
                ext,0,&dfWindow::mf_WndProc,attrib);
    assert(m_Wnd != 0);
}
bool dfWindow::mf_WndProc(WINDOW wnd, MESSAGE msg, PARAM pr1, PARAM pr2)
{
    bool rtn = false;
    dfWindow *This;
    if (wnd->wrapper == 0)  {
        assert(mp_cwindow != 0);
        wnd->wrapper = mp_cwindow;
        mp_cwindow->m_Wnd = wnd;
        mp_cwindow = 0;
    }
    This = (dfWindow*)(wnd->wrapper);
    This->p1 = pr1;
    This->p2 = pr2;
    This->m_msg = msg;
    if (msg == COMMAND && (pr2 == 0 || pr2 == LB_CHOOSE || pr2 ==LB_SELECTION))
        rtn = This->DispatchDFMessage((MESSAGE)pr1);
    else
        rtn = This->DispatchDFMessage(msg);
    // --- done again because of reentrant calls
    This->p1 = pr1;
    This->p2 = pr2;
    This->m_msg = msg;
    if (rtn && msg == CLOSE_WINDOW)
        This->m_Wnd = 0;
    return rtn;
}
void dfWindow::SetWndRectangle(Rect rc)
{
    m_Wnd->rc = rc;
    m_Wnd->ht = rc.Height();
    m_Wnd->wd = rc.Width();
}
void dfWindow::WriteChar(int ch)
{
    SetStandardColor(m_Wnd);
    PutWindowChar(m_Wnd, ch, m_Wnd->CurrCol, m_Wnd->WndRow);
}
void dfWindow::PositionCursor(int x, int y)
{
    SetColumn(x);
    SetRow(y);
    SetCursor();
}
void dfWindow::CursorUp(int ct)
{
    while (ct-- && m_Wnd->WndRow) {
        m_Wnd->WndRow--;
        SetCursor();
    }
}
void dfWindow::CursorDown(int ct)
{
    while (ct-- && m_Wnd->WndRow < ClHeight())  {
        m_Wnd->WndRow++;
        SetCursor();
    }
}
void dfWindow::CursorRight(int ct)
{
    while (ct-- && m_Wnd->CurrCol < ClWidth()) {
        m_Wnd->CurrCol++;
        SetCursor();
    }
}
void dfWindow::CursorLeft(int ct)
{
    while (ct-- && m_Wnd->CurrCol) {
        m_Wnd->CurrCol--;
        SetCursor();
    }
}
void dfWindow::ClearScreen()
{
    SendMessage(CLEARTEXT);
    Paint();
    PositionCursor(0, 0);
}
void dfWindow::ClearToEOL()
{
    int col = m_Wnd->CurrCol;
    SetStandardColor(m_Wnd);
    while (col < ClWidth())
        PutWindowChar(m_Wnd, ' ', col++, m_Wnd->WndRow);
}


Listing Five


// --------- dfappl.h
#ifndef DFAPPL_H
#define DFAPPL_H

#include <fstream.h>

class dfApplication : public dfWindow {
    int m_currx, m_curry;
    MBAR& menu;
protected:
    MAPDEF(dfWindow)
    virtual bool LoadConfig()
        { return ::LoadConfig(); }
    virtual void SaveConfig()
        { ::SaveConfig(); }
    bool OnOpen();
    bool OnClose();
public:
    dfApplication(const string& ttl, MBAR& mnu,
        const Rect& rc = Rect(0,0,-1,-1));
    virtual ~dfApplication();
    void Run();
    void WriteStatus(const string& tx);
    void ClearStatus();
    bool GetCommandToggle(commands id)
        { return (bool) ::GetCommandToggle(&menu, id); }
    void SetCommandToggle(commands id)
        { ::SetCommandToggle(&menu, id); }
    void ClearCommandToggle(commands id)
        { ::ClearCommandToggle(&menu, id); }
};
#endif


Listing Six


// ------- dfappl.cpp
#include "dfwrap.h"

MSGMAP(dfApplication)
    MSG(OPEN_WINDOW,  OnOpen)
    MSG(CLOSE_WINDOW, OnClose)
ENDMAP

dfApplication::dfApplication(const string& ttl, MBAR& mnu, const Rect& rc) :
        dfWindow(APPLICATION, ttl, rc, &mnu, HASSTATUSBAR), menu(mnu)
{
    curr_cursor(&m_currx, &m_curry);
    LoadHelpFile(DFlatApplication);
    ResetAttribute(CONTROLBOX);
    m_Wnd->condition = ISMAXIMIZED;
}
dfApplication::~dfApplication()
{
    cursor(m_currx, m_curry);
}
bool dfApplication::OnOpen()
{
    if (!LoadConfig())
        cfg.ScreenLines = SCREENHEIGHT;
    return true;
}
bool dfApplication::OnClose()
{
    DispatchBaseDFMessage(CLOSE_WINDOW);
    SaveConfig();
    return false;
}
void dfApplication::Run()
{
    SendMessage(SETFOCUS, TRUE, 0);
    while (dispatch_message())
        ;
}
void dfApplication::WriteStatus(const string& tx)
{
    ::SendMessage(m_Wnd->StatusBar, SETTEXT, (PARAM) tx.c_str(), 0);
    ::SendMessage(m_Wnd->StatusBar, PAINT, 0, 0);
}
void dfApplication::ClearStatus()
{
    ::SendMessage(m_Wnd->StatusBar, CLEARTEXT, 0, 0);
    ::SendMessage(m_Wnd->StatusBar, PAINT, 0, 0);
}





Listing Seven


// ---------- dfdial.h
#ifndef DFDIAL_H
#define DFDIAL_H

#include <cstring.h>
#include "icmds.h"

class dfDialog : public dfWindow    {
    DBOX m_dbox;
    bool OnClose();
    bool OnSize();
    bool OnMove();
    bool OnLBChoose();
protected:
    MAPDEF(dfWindow)
    bool KeepLBSelectionInView(cmds id);
    bool m_isrunning;
public:
    dfDialog(DBOX& db) : m_dbox(db), m_isrunning(false)
        { }
    virtual ~dfDialog()
        { }
    bool doModal();
    void doModeless();
    bool isRunning() const
        { return m_isrunning; }
    void SetTitle(const char *title);
    void SetControlFocus(cmds id);
    void SetControlText(cmds id, const char *t, CLASS cl = (CLASS)-1);
    const char *GetControlText(cmds id, CLASS cl = (CLASS)-1);
    int SendCtlMessage(cmds id, MESSAGE msg, PARAM pr1=0, PARAM pr2=0);
    void SetWindowText(cmds id, const char *t)
        { SendCtlMessage(id, SETTEXT, (PARAM) t); }
    char *GetWindowText(cmds id) const;
    void AddWindowText(cmds id, const char *t)
        { SendCtlMessage(id, ADDTEXT, (PARAM) t); }
    bool TextChanged(cmds id);
    void ClearWindowText(cmds id)
        { SendCtlMessage(id, CLEARTEXT); }
    int GetLineCount(cmds id);
    int GetLBSelection(cmds id)
        { return SendCtlMessage(id, LB_CURRENTSELECTION); }
    void SetLBSelection(cmds id, int sel)
        { SendCtlMessage(id, LB_SETSELECTION, sel); }
    void GetLBTextLine(cmds id, int lno, char *t)
        { SendCtlMessage(id, LB_GETTEXT, (PARAM) t, lno); }
    void GetSelectedLBText(cmds id, char *t);
    int  DeleteSelectedLBLine(cmds id, char *prompt = 0);
    void InsertTextLine(cmds id, int lno, char *t)
        { SendCtlMessage(id, INSERTTEXT, (PARAM) t, lno); }
    void DeleteTextLine(cmds id, int lno)
        { SendCtlMessage(id, DELETETEXT, lno); }
    void PaintControl(cmds id)
        { SendCtlMessage(id, PAINT); }
    void MoveControl(cmds id, int x, int y);
    void SizeControl(cmds id, int h, int w);
    void SetControlOn(cmds id);
    void SetControlOff(cmds id);
    bool GetControlSetting(cmds id);
    void EnableCommandButton(cmds id)
        { EnableButton(&m_dbox, (commands) id); }
    void DisableCommandButton(cmds id)
        { DisableButton(&m_dbox, (commands) id); }
    void SetProtection(cmds id);
    void SetControlWindowAttribute(cmds id, int attrib);
    void ResetControlWindowAttribute(cmds id, int attrib);
    bool isEmpty(cmds id) const;
    const DBOX& DBox() const
        { return m_dbox; }
    void DialogTextCopy(char *s1, cmds id, int len);
};
class DialogConfig  {
    bool max;               // true if DB was maximized
    Rect rc;                // DB position, size
    Rect rrc;               // DB restored configuration
    Rect ctls[MAXCONTROLS]; // control positions, sizes
    int restattrib;         // restored attribute
public:
    DialogConfig() : max(false)
        { }
    void SaveDialog(dfDialog& db);
    void RestoreDialog(dfDialog& db);
};
#endif


Listing Eight


// ----- dfdial.cpp
#include "dfwrap.h"

MSGMAP(dfDialog)
    MSG(LB_CHOOSE,    OnLBChoose)
    MSG(SIZE,         OnSize)
    MSG(MOVE,         OnMove)
    MSG(CLOSE_WINDOW, OnClose)
ENDMAP

bool dfDialog::OnLBChoose()
{
    PostCommand(ID_OK);
    return true;
}
bool dfDialog::OnClose()
{
    m_isrunning = false;
    return true;
}
bool dfDialog::doModal()
{
    mp_cwindow = this;
    m_isrunning = true;
    return DialogBox(0, &m_dbox, TRUE, mf_WndProc);
}
void dfDialog::doModeless()
{
    mp_cwindow = this;
    m_isrunning = true;
    DialogBox(0, &m_dbox, FALSE, mf_WndProc);
}
void dfDialog::SetTitle(const char *title)
{
    m_dbox.dwnd.title = (char *) title;
    if (m_isrunning)
        SendMessage(BORDER);
}
bool dfDialog::OnMove()
{
    m_dbox.dwnd.x = (int) p1;
    m_dbox.dwnd.y = (int) p2;
    return true;
}
bool dfDialog::OnSize()
{
    m_dbox.dwnd.x = Left();
    m_dbox.dwnd.y = Top();
    m_dbox.dwnd.w = p1-Left()+1;
    m_dbox.dwnd.h = p2-Top()+1;
    return true;
}
void dfDialog::SetControlText(cmds id, const char *t, CLASS cl)
{
    SetDlgTextString(&m_dbox, (commands) id, (char*) t, cl);
}
const char* dfDialog::GetControlText(cmds id, CLASS cl)
{
    return GetDlgTextString(&m_dbox, (commands) id, cl);
}
void dfDialog::SetControlFocus(cmds id)
{
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)
        ::SendMessage(cwnd, SETFOCUS, TRUE, 0);
}
int dfDialog::SendCtlMessage(cmds id, MESSAGE msg, PARAM pr1, PARAM pr2)
{
    int rtn = -1;
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)  {
        MESSAGE svmsg = m_msg;
        PARAM svp1 = p1;
        PARAM svp2 = p2; 
        rtn = ::SendMessage(cwnd, msg, pr1, pr2);
        p1 = svp1;
        p2 = svp2;
        m_msg = svmsg;
    }
    return rtn;
}
char* dfDialog::GetWindowText(cmds id) const
{
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)
        return GetText(cwnd);
    return 0;
}
bool dfDialog::TextChanged(cmds id)
{
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)
        return (bool) (cwnd->TextChanged);
    return false;
}
int dfDialog::GetLineCount(cmds id)
{
    int ct = 0;
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)
        ct = GetTextLines(cwnd);
    return ct;
}
void dfDialog::GetSelectedLBText(cmds id, char *t)
{
    int sel = GetLBSelection(id);
    if (sel != -1)
        GetLBTextLine(id, sel, t);
    else
        *t = '\0';
}
int dfDialog::DeleteSelectedLBLine(cmds id, char *prompt)
{
    int count = GetLineCount(id);
    if (count > 0)  {
        int sel = GetLBSelection(id);
        if (sel != -1)  {
            if (prompt == 0 || YesNoBox(prompt))    {
                DeleteTextLine(id, sel);
                if (sel == count-1)
                    SetLBSelection(id, sel-1);
                KeepLBSelectionInView(id);
                PaintControl(id);
                SetControlFocus(id);
                return sel;
            }
        }
    }
    return -1;
}
void dfDialog::MoveControl(cmds id, int x, int y)
{
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)  {
        int oldx = FindCommand(&m_dbox, (commands) id, -1)->dwnd.x;

        int oldy = FindCommand(&m_dbox, (commands) id, -1)->dwnd.y;
        x = x == -1 ? oldx : x;
        y = y == -1 ? oldy : y;
        int difx = x - oldx;
        int dify = y - oldy;
        if (difx || dify)   {
            FindCommand(&m_dbox, (commands) id, -1)->dwnd.x = x;
            FindCommand(&m_dbox, (commands) id, -1)->dwnd.y = y;
            SendCtlMessage(id, MOVE, GetLeft(cwnd)+difx, GetTop(cwnd)+dify);
        }
    }
}
void dfDialog::SizeControl(cmds id, int h, int w)
{
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)  {
        FindCommand(&m_dbox, (commands) id, -1)->dwnd.h = h;
        FindCommand(&m_dbox, (commands) id, -1)->dwnd.w = w;
        SendCtlMessage(id, SIZE, GetLeft(cwnd)+w-1, GetTop(cwnd)+h-1);
    }
}
void dfDialog::SetControlOn(cmds id)
{
    CTLWINDOW *ct = FindCommand(&m_dbox, (commands) id, RADIOBUTTON);
    if (ct != 0)
        PushRadioButton(&m_dbox, (commands) id);
    else
        SetCheckBox(&m_dbox, (commands) id);
    PaintControl(id);
}
void dfDialog::SetControlOff(cmds id)
{
    ClearCheckBox(&m_dbox, (commands) id);
    PaintControl(id);
}
bool dfDialog::GetControlSetting(cmds id)
{
    CTLWINDOW *ct = FindCommand(&m_dbox, (commands) id, RADIOBUTTON);
    if (ct != 0)
        return RadioButtonSetting(&m_dbox, (commands) id);
    return CheckBoxSetting(&m_dbox, (commands) id);
}
void dfDialog::SetProtection(cmds id)
{
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)
        SetProtected(cwnd);
}
bool dfDialog::KeepLBSelectionInView(cmds id)
{
    bool rtn = false;
    int sel = SendCtlMessage(id, LB_CURRENTSELECTION);
    if (sel != -1)  {
        WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
        int top = cwnd->wtop;
        int ht = ClientHeight(cwnd) - 1;
        int bottom = top + ht;
        if ((rtn = (sel < top)) == true)
            cwnd->wtop = sel;
        else if ((rtn = (sel > bottom)) == true)
            cwnd->wtop = sel - ht;
    }
    return rtn;
}
void dfDialog::SetControlWindowAttribute(cmds id, int attrib)
{
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)
        AddAttribute(cwnd, attrib);
}
void dfDialog::ResetControlWindowAttribute(cmds id, int attrib)
{
    WINDOW cwnd = ControlWindow(&m_dbox, (commands) id);
    if (cwnd != 0)
        ClearAttribute(cwnd, attrib);
}
bool dfDialog::isEmpty(cmds id) const
{
    const char *p_txt = GetWindowText(id);
    if (p_txt)
        while (*p_txt)  {
            if (*p_txt != ' ' && *p_txt != '\n')
                return false;
            p_txt++;
        }
    return true;
}
void dfDialog::DialogTextCopy(char *s1, cmds id, int len)
{
    const char *s2 = GetWindowText(id);
    if (s1 != 0 && s2 != 0)
        while (*s2 && *s2 != '\n' && --len)
            *s1++ = *s2++;
        *s1 = '\0';
}
void DialogConfig::RestoreDialog(dfDialog& db)
{
    if (rc.Width() > 1) {
        db.SetWndRectangle(rc);
        db.SetRestoredRect(rrc);
        db.SetRestoredAttrib(restattrib);
        DBOX& dbox = const_cast<DBOX&>(db.DBox());
        dbox.dwnd.x = rc.Left();
        dbox.dwnd.y = rc.Top();
        dbox.dwnd.h = rc.Height();
        dbox.dwnd.w = rc.Width();
        if (max)
            // --- DB was maximized when last used
            db.SetWndCondition(ISMAXIMIZED);
        for (int i = 0; i < MAXCONTROLS; i++)   {
            dbox.ctl[i].dwnd.x = ctls[i].Left();
            dbox.ctl[i].dwnd.y = ctls[i].Top();
            dbox.ctl[i].dwnd.h = ctls[i].Height();
            dbox.ctl[i].dwnd.w = ctls[i].Width();
        }
    }
}
void DialogConfig::SaveDialog(dfDialog& db)
{
    rc =   db.WndRectangle();
    rrc =  db.RestoredRect();
    max = db.isMaximized();
    restattrib = db.GetRestoredAttrib();
    for (int i = 0; i < MAXCONTROLS; i++)   {
        const DIALOGWINDOW& dwnd = db.DBox().ctl[i].dwnd;
        ctls[i] = Rect(dwnd.x, dwnd.y, dwnd.h, dwnd.w);
    }
}


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