home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
PROG_GEN
/
FACETV.ZIP
/
DLG_BOX.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-04
|
28KB
|
991 lines
/************************************************************************
**
** @(#)dlg_box.cpp 11/11/93 Chris Ahlstrom
**
** ------------------------
** 73340.26!compuserve.com
** ------------------------
**
** Defines the DialogBox class. This class is similar to the
** TBox class, but it is meant to be a base class for a single
** dialog box, not for a whole application. Furthermore, it is meant
** to isolate the dialog box from the user interface, so that either
** Windows or TurboVision (DOS) modes can be supported with almost
** equal ease. Finally, if possible, I will simplify the class
** a bit.
**
** It provides all of the functionality of the DialogBox class, plus
** it includes support for new dialog box functions.
**
** The DialogBox class will become the primary dialog boxes for
** all my C++ programs. Lets almost all important parameters be set.
**
** 1. A function called "doDialog()", that can read any old
** MainBox structure (see tv_menu.h), link it with a
** GenParameters structure, and display it all as a dialog
** box.
**
** 2. A "GenParameters" structure pointer; it points to any
** kind of structure that contains data for a dialog box.
**
*************************************************************************/
/************************************************************************
**
** For information on the slightly strange method used for coordinates,
** see pp. 94-95 of the Turbo Vision manual. Here's a quick summary:
**
** The UL (upper left) coordinate of a rectangle is as normal
** (e.g. [0,0] is the upper-left-most you can go). But the LR (lower
** right is one higher than normal, so that the LR corner of a unit-sized
** box as far up and left as possible is [1,1], not [0,0].
**
** Thus, the LR coordinate is given by
**
** LR = UL + box_size
**
** instead of by LR = UL + box_size - [1,1]
**
** However, for the databoxes, we must add 2 characters to the
** x-dimension of the box, to allow for the two exta spaces at the
** left and right of the databox. Otherwise, when the menu first comes
** up, only part of each field's data can be seen.
**
** The primary dialog boxes for C++\TV programs for general purposes.
** It is very similar to dialgbox.cpp, with the following exceptions:
**
** 0. The relevant dialog box is converted to user format for
** display.
**
** 1. Also supports , UnsignedByte, Checkbyte, and Radiobyte
** data types.
**
** 2. The following values are retrieved from a special list,
** when the correct Extended type is provided:
**
** a. minimum and maximum values
** b. conversions between user units and machine (internal) units
** c. the kind of dialog field to use.
** d. display sizes
**
** 3. Once read, and altered, the dialog box data is copied to
** the specified place, being transformed to machine units
** along the way.
**
** Defines a base class to provide a single function.
**
** 1. A function called "doDialog()", that can read any old
** MainBox structure (see tv_menu.h), link it with a
** genParameters structure, and display it all as a dialog
** box.
**
** 2. A "genParameters" structure pointer; it points to any
** kind of structure that contains data for a dialog box.
**
** 3. As part of the class, just one array of ExtendedDescriptors
** that describe data that will have a linear mapping applied
** to it before displaying, and the inverse linear mapping
** applied after editing. (See the TINPUT modules.)
**
*************************************************************************/
#define DLG_BOX_cpp
#include <stdio.h>
#include "boxtools.h" // string box sizing
#include "dlg_box.h" // declarations of this class
#include "tv_menu.h" // the C++ version
#include "tinput.h" // "Variations on TInputLine in C++", Op. 2.01
#include "tinpmous.h" // "Variations on TInputLine in C++", Op. 3.03
static MouseMap defaultMouseMap =
{
LINEAR,
1000,
100,
NULL
};
/************************************************************************
** DialogBox constructors
**
** We only need two versions of the constructor to support
** using either the ExtendedDescriptor, the MappedFieldDescriptor,
** none, or both.
**
*************************************************************************/
DialogBox::DialogBox
(
MainBox *d, // points to nested structure (tv_menu.h)
GeneralDialogData dialog, // pointer to general dialog structure
int size, // number of bytes in the data
TDeskTop *desk,
ExtendedDescriptor *datamapping,
MappedFieldDescriptor *fieldmapping
) :
dialogWindow (d),
dialogData (dialog),
boxTop (desk),
dataMap (datamapping),
useDataMap (datamapping != NULL),
fieldMap (fieldmapping),
useFieldMap (fieldmapping != NULL),
dataSize (size)
{
if (useFieldMap)
mappedWindow = (MappedBox *) dialogWindow;
}
DialogBox::DialogBox
(
MainBox *d, // points to nested structure (tv_menu.h)
GeneralDialogData dialog, // pointer to general dialog structure
int size, // number of bytes in the data
TDeskTop *desk,
MappedFieldDescriptor *fieldmapping
) :
dialogWindow (d),
dialogData (dialog),
boxTop (desk),
dataMap (NULL),
useDataMap (0),
fieldMap (fieldmapping),
useFieldMap (fieldmapping != NULL),
dataSize (size)
{
if (useFieldMap)
mappedWindow = (MappedBox *) dialogWindow;
}
DialogBox::~DialogBox ()
{
}
/************************************************************************
** DialogBox::doDialog
**
** Takes an relatively complex data structure that describes
** a dialog box, and makes the Turbo Vision tree for that box.
**
** Adding the radio boxes: First we set up a linked list of all
** the labels that are provided elsewhere. Then we make a radio-box
** using this list. We work backwards, to maintain the desired linear
** appearance.
**
** Returns True if the user edited the box and accepted the edit.
** (Note that OK does not become the default unless the box is edited,
** or the user Tabs to the OK box).
**
*************************************************************************/
Boolean
DialogBox::doDialog (void)
{
Boolean err = False;
Boolean rcode = False;
/*********************************************************************
** Create the box part of the dialog.
**********************************************************************/
TPoint ul = dialogWindow->box.location; // location of dialog
TPoint lr = dialogWindow->box.size; // size of dialog
lr += ul; // boundaries of dialog
TDialog *pd = new TDialog(TRect(ul,lr), dialogWindow->box.title);
/*********************************************************************
** Add elements inside the box. First, scan the list of data-items.
** For each one, determine its type (e.g. numeric or Radiobox),
** and insert it. Then tack on the "OK/cancel" buttons.
**********************************************************************/
if (pd) // did it work above?
{
MenuField *field = dialogWindow->fields; // point to first field
TView *b; // general-purpose pointer
for (int f = 0; f < dialogWindow->fieldcount; f++, field++)
{
TextLabel *lab = &field->label; // label structure in field
TextData *dat = &field->data; // data structure in field
DataType data_type = dat->datatype; // primary data type
char *dtmplate = dat->dtmplate; // string for template
char *format = dat->format; // format for display
/************************************************************
** Obtain the kind of "spec" to use, based on data type.
** Note that the little group of declarations below provides
** one declaration for each member of the TextDataSpec
** union defined in tv_menu.h.
*************************************************************/
char **strings = NULL;
ExtendedField dtype = BOGUS_EXTENDED_FIELD;
MouseMap *map = NULL;
if (isBox(data_type))
{
strings = dat->spec.strings; // strings for display
}
else if (isControl(data_type))
{
map = dat->spec.ctrlmap; // mouse/key mapping
}
else if (isExtended(data_type))
{
dtype = dat->spec.dtype; // extension of int, etc?
}
/************************************************************
** If the data type is Extended, then we must get a little
** more information from a ExtendedDescriptor list.
** Right now, the only information we get is the fundamental
** data type and the user's range. If something is screwy,
** we can only skip this field.
*************************************************************/
Range user;
Range code;
int mapped;
code.minimum = dat->code.minimum;
code.maximum = dat->code.maximum;
user = code; // in case of screwups
if (isExtended(data_type))
{
if (useDataMap)
{
if (dataMap[dtype].type == dtype) // consistent?
{
user = dataMap[dtype].user;
data_type = dataMap[dtype].datatype;
mapped = TINPUT_EXTENDED;
}
else // inconsistent!
break; // don't bother
}
else
break; // don't bother
}
else
{
mapped = TINPUT_NO_EXTENDED;
}
/************************************************************
** For data, use the length of the template string (or
** strings, in the case of Box/Button items) provided,
** plus 2 extra characters for the scroll arrows, if needed.
** The length of the data-item must be exact, or weird
** things happen in the bowels of TInputLine.
** Selecting all these numbers became quite an art!!!
*************************************************************/
TPoint dsz; // adjusted entry-box size
int maxlength; // maxLen for TInputLine(s)
if (isBox(data_type))
{
dsz = banner_list_size // templates of sorts
(
strings, RADIOBUTTON_PAD
);
maxlength = 0; // unused for box data
}
else if (isString(data_type))
{
dsz = banner_size(dtmplate, SCROLLARROW_PAD);
maxlength = strlen(dtmplate) + SCROLLARROW_PAD - 1;
}
else if (isNumeric(data_type))
{
dsz = banner_size(dtmplate, SCROLLARROW_PAD);
maxlength = dsz.x; // buffer size
}
/************************************************************
** Insert current data-item inside the dialog box,
** relative to its label (we'll insert that later).
** Obtain label's dimensions by inspection, to make
** it easier on the poor programmer.
*************************************************************/
TPoint lul = lab->location; // label location
TPoint llr; // size/corner of data label
TPoint dul = dat->location; // data offset
TPoint dlr; // adjusted data size
llr = banner_size(lab->string, LABEL_PAD); // size with padding
llr += lul; // lower-right corner
dul += lul; // move it relative to label
dlr = dsz + dul; // get lower-right corner
dlr.x++; // allow proper viewing
b = makeField
(
data_type, strings, dul, dlr, maxlength,
code, format, mapped, user, *map
);
if (b)
{
pd->insert(b); // insert the databox
if
(
lab->string != NULL && // no string given?
*lab->string != '\0' // empty string given?
)
{
TView *c = new TLabel(TRect(lul, llr), lab->string, b);
if (c)
pd->insert(c); // insert the label
}
}
else
{
err = True;
break;
}
}
if (err != True)
{
buttonsOKCancel // insert OK/Cancel buttons
(
pd, dialogWindow->box.size
);
if (dialogWindow->box.string != NULL)
{
ul = dialogWindow->box.labelloc; // Insert main string
lr.x = strlen(dialogWindow->box.string) + 1;
lr.y = 1;
lr += ul;
TView *b = new TStaticText
(
TRect(ul, lr), dialogWindow->box.string
);
if (b)
pd->insert(b);
}
/************************************************************
** Save the dialog data and read it back when the dialog box
** is successfully closed. The setData() member of all
** sub-objects gets called (deep inside Turbo Vision).
*************************************************************/
pd->setData(dialogData); // fill in data boxes
ushort control = boxTop->execView(pd);
if (control != cmCancel)
{
pd->getData(dialogData); // obtain data from data boxes
rcode = True;
}
}
else
{
TView *b = new TStaticText(TRect(4,4,16,5), "DIALOG ERROR");
if (b)
pd->insert(b);
boxTop->execView(pd);
}
TObject::destroy(pd); // remove the box now
}
return rcode;
}
/************************************************************************
** mappedDialog()
**
** Takes an relatively complex data structure that describes
** a dialog box, and makes the Turbo Vision tree for that box.
**
** Adding the radio boxes: First we set up a linked list of all
** the labels that are provided elsewhere. Then we make a radio-box
** using this list. We work backwards, to maintain the desired linear
** appearance.
**
** It's really the same as doDialog(), but gets the same information
** from different place (places!)
**
*************************************************************************/
Boolean
DialogBox::mappedDialog ()
{
Boolean err = False;
Boolean rcode = False;
/*********************************************************************
** Create the box part of the dialog.
**********************************************************************/
TPoint ul = dialogWindow->box.location; // location of dialog
TPoint lr = dialogWindow->box.size; // size of dialog
lr += ul; // boundaries of dialog
TDialog *pd = new TDialog(TRect(ul,lr), dialogWindow->box.title);
/*********************************************************************
** Add elements inside the box. First, scan the list of data-items.
** For each one, determine its type (e.g. numeric or Radiobox),
** and insert it. Then tack on the "OK/cancel" buttons.
**********************************************************************/
if (pd) // did it work above?
{
MappedField *field = mappedWindow->fields; // point to first field
TView *b; // general-purpose pointer
for (int f = 0; f < mappedWindow->fieldcount; f++, field++)
{
/************************************************************
** Unlike for doDialog(), the data here is always mapped.
** mappedDialog() always inspects the desired data-type to
** get the information that describes the data field.
*************************************************************/
int mapped = TINPUT_NO_EXTENDED; // assume the worst at first
Range user; // range of user-viewed units
Range code; // range of internal units
DataType data_type; // type of data (e.g. Integer)
char *dtmplate; // data template string
char *format; // data format for display
char **strings; // list of items for boxes
if (useFieldMap)
{
MappedFieldDescriptor *map; // data-type description
map = &fieldMap[field->dtype];
if (map->type == field->dtype)
{
mapped = TINPUT_EXTENDED;
data_type = map->datatype;
code = map->code;
user = map->user;
dtmplate = map->dtmplate;
format = map->format;
strings = map->strings;
}
else // disordered mapping-list
break; // don't show the field
}
else // no mapping-list provided
break; // don't show the field
/************************************************************
** For data, use the length of the template string (or
** strings, in the case of Box/Button items) provided,
** plus 2 extra characters for the scroll arrows, if needed.
*************************************************************/
TPoint dsz; // adjusted entry-box size
int maxlength; // maxLen for TInputLine(s)
dsz.x = 1; // bogus size in case of...
dsz.y = 1; // ...problems
if (isBox(data_type))
{
if (strings != NULL)
{
dsz = banner_list_size // templates of sorts
(
strings, RADIOBUTTON_PAD
);
}
maxlength = 0; // unused for box data
}
else if (isString(data_type))
{
if (dtmplate != NULL)
{
dsz.x = strlen(dtmplate) + SCROLLARROW_PAD;
dsz.y = 1; // strong assumption!
maxlength = dsz.x - 1; // includes 1 null
}
else
{
maxlength = dsz.x; // includes 1 null
}
}
else if (isNumeric(data_type))
{
if (dtmplate != NULL)
{
dsz.x = strlen(dtmplate) + SCROLLARROW_PAD;
dsz.y = 1; // strong assumption!
}
maxlength = dsz.x; // buffer size
}
/************************************************************
** Insert the current data-item inside the dialog box,
** relative to its label (we'll insert that later).
** Obtain the label's dimensions by inspection, to make
** it easier on the poor programmer.
*************************************************************/
TPoint lul = field->label_location; // label location
TPoint llr; // size of data label
TPoint dul = field->data_location; // data offset
TPoint dlr; // adjusted data size
llr.x = (int) strlen(field->label_string) + LABEL_PAD;
llr.y = 1;
llr += lul; // lower-right corner
dul += lul; // move it relative to label
dlr = dsz + dul; // get lower-right corner
dlr.x++; // allow proper viewing
b = makeField
(
data_type, strings, dul, dlr, maxlength,
code, format, mapped, user, defaultMouseMap
);
if (b)
{
pd->insert(b); // insert the databox
if
(
field->label_string != NULL && // no string?
*field->label_string != '\0' // empty string?
)
{
TView *c = new TLabel
(
TRect(lul, llr), field->label_string, b
);
if (c)
pd->insert(c); // insert the label
}
}
else
{
err = True;
break;
}
}
if (err != True)
{
buttonsOKCancel // insert OK/Cancel buttons
(
pd, mappedWindow->box.size
);
if (mappedWindow->box.string != NULL) // insert main string
{
ul = mappedWindow->box.labelloc;
lr.x = strlen(mappedWindow->box.string) + 1;
lr.y = 1;
lr += ul;
TView *b = new TStaticText
(
TRect(ul, lr), mappedWindow->box.string
);
if (b)
pd->insert(b);
}
/************************************************************
** Save the dialog data and read it back when the dialog box
** is successfully closed.
*************************************************************/
pd->setData(dialogData);
ushort control = boxTop->execView(pd);
if (control != cmCancel)
{
pd->getData(dialogData);
rcode = True;
}
}
else
{
TView *b = new TStaticText(TRect(4,4,16,5), "DIALOG ERROR");
if (b)
pd->insert(b);
boxTop->execView(pd);
}
TObject::destroy(pd); // remove the box now
}
return rcode;
}
/************************************************************************
** DialogBox::makeField()
**
** Determines the type of data-field display item to make, and
** makes it. Returns a pointer to it.
**
*************************************************************************/
TView *
DialogBox::makeField
(
DataType data_type, // exact type of data item
char **strings, // pointer to list of button-strings
TPoint dul, // upper-left corner of the box
TPoint dlr, // lower-right corner of the box
int maxlength, // for TInputLine(s)
Range& code, // internal range of the units
char *format, // format of the display
int mapped, // non-zero if data mapping is to be employed
Range& user, // user-viewable range of the units
MouseMap &map // how the mouse and arrows will be mapped
)
{
TView *b;
switch (data_type) // create the databox
{
case Checkbox:
case Radiobox:
case Checkbyte:
case Radiobyte:
b = makeButtons(data_type, strings, dul, dlr);
break;
case UnsignedByte:
b = new TInputByte
(
TRect(dul, dlr), maxlength, code, format, mapped, user
);
break;
case SignedByte:
b = new TInputSignedByte
(
TRect(dul, dlr), maxlength, code, format, mapped, user
);
break;
case Integer:
case Hex:
b = new TInputInteger
(
TRect(dul, dlr), maxlength, code, format, mapped, user
);
break;
case LongInteger:
case HexAddress:
b = new TInputLong
(
TRect(dul, dlr), maxlength, code, format, mapped, user
);
break;
case Float:
b = new TInputFloat
(
TRect(dul, dlr), maxlength, code, format, mapped, user
);
break;
case FloatControl:
b = new TInputFloatControl
(
TRect(dul, dlr), maxlength, code, format, mapped, user,
map.mapType, map.maxMickey, map.maxKey, map.ctrlFunction
);
break;
case ByteString:
b = new TByteString(TRect(dul, dlr), maxlength);
break;
case TimeField:
case DateField:
case String:
case Money:
default:
b = new TInputLine(TRect(dul, dlr), maxlength);
break;
}
return b;
}
/************************************************************************
** DialogBox::makeButtons()
**
** Given that the list is pointing to a boxy data-item,
** this routine makes up a new box.
**
*************************************************************************/
TView *
DialogBox::makeButtons
(
DataType datatype, // the exact type of boxy data item
char **list, // pointer to list of button-strings
TPoint dul, // upper-left corner of the box
TPoint dlr // lower-right corner of the box
)
{
TView *b; // the pointer to make
int count; // count the entries
TSItem *next; // last/next in box lists
count = 0; // count the entries
next = 0; // last/next in box lists
while (*list != NULL) // stop at a NULL pointer
{
list++; // next entry in the list
count++; // count it
}
list--; // ignore trailing NULL
for (; count > 0; count--, list--) // build linked list
{
next = new TSItem(*list, next);
}
switch (datatype)
{
case Checkbox:
b = new TCheckBoxes(TRect(dul, dlr), next);
break;
case Radiobox:
b = new TRadioButtons(TRect(dul, dlr), next);
break;
case Checkbyte:
b = new TByteCheckBoxes(TRect(dul, dlr), next);
break;
case Radiobyte:
b = new TByteRadioButtons(TRect(dul, dlr), next);
break;
}
return b;
}
/************************************************************************
** DialogBox::buttonsOKCancel
**
** Simply inserts the OK and Cancel buttons at a good spot at
** the bottom of the box.
**
*************************************************************************/
#define OK_SIZE 9 // size of each OK/Cancel bar
#define OK_SPACE 4 // distance between each bar
#define OK_THICK 2 // thickness of the bars
#define OK_LENGTH 2*(OK_SIZE+OK_SPACE) // total length of OK/Cancel bars
#define OK_BOTTOM 1 // distance of bar from bottom
#define OK_ADJUST 5 // another magic number
void
DialogBox::buttonsOKCancel
(
TDialog *pd,
TPoint lr
)
{
TPoint ul; // location of dialog
TView *b; // temporary pointer
ul.x = (lr.x - OK_LENGTH) / 2; // center bars horizontally
ul.y = lr.y - OK_THICK - OK_BOTTOM; // up a little from bottom
lr.x = ul.x + OK_SIZE + 2; // right end of OK bar
lr.y = ul.y + OK_THICK; // allow for thickness of bar
b = new TButton(TRect(ul,lr), "~O~K", cmOK, bfDefault);
if (b)
pd->insert(b);
ul.x += OK_SIZE + OK_ADJUST;
lr.x += OK_SIZE + OK_ADJUST;
b = new TButton(TRect(ul,lr), "~C~ancel", cmCancel, bfNormal);
if (b)
pd->insert(b);
}
/************************************************************************
** DialogBox::isBox()
** DialogBox::isString()
** DialogBox::isNumeric()
** DialogBox::isExtended()
** DialogBox::isControl()
**
** Each returns a 1 if the DataType is one of the right kind of
** data item.
**
*************************************************************************/
Boolean
DialogBox::isBox
(
DataType datatype
)
{
return (Boolean)
(
(datatype == Checkbox) ||
(datatype == Radiobox) ||
(datatype == Checkbyte) ||
(datatype == Radiobyte)
);
}
Boolean
DialogBox::isNumeric
(
DataType datatype
)
{
return (Boolean)
(
(datatype == Integer) ||
(datatype == Hex) ||
(datatype == UnsignedByte) ||
(datatype == SignedByte) ||
(datatype == LongInteger) ||
(datatype == HexAddress) ||
(datatype == Float) ||
(datatype == FloatControl)
);
}
Boolean
DialogBox::isString
(
DataType datatype
)
{
return (Boolean)
(
(datatype == ByteString) ||
(datatype == TimeField) ||
(datatype == DateField) ||
(datatype == String) ||
(datatype == Money)
);
}
Boolean
DialogBox::isControl
(
DataType datatype
)
{
return (Boolean)
(
(datatype == FloatControl)
);
}
Boolean
DialogBox::isExtended
(
DataType datatype
)
{
return (Boolean)
(
(datatype == Extended)
);
}
/************************************************************************
** write()
** read()
**
** Saves and restores all the dialog boxes; they amount to all
** the configuration we need.
**
** I should learn to use streams on this stuff; later dude.
**
*************************************************************************/
#define ERR_NONE 0
#define ERR_FILE_WRITE 1
#define ERR_FILE_READ 2
#define ERR_FILE_NOT_OPEN 3
int
DialogBox::write
(
FILE *fptr
)
{
size_t items; // number of items successfully written
int errcode = ERR_NONE;
if (fptr != NULL)
{
items = fwrite(dialogData, dataSize, 1, fptr);
if (items != dataSize)
errcode = ERR_FILE_WRITE;
}
else
{
errcode = ERR_FILE_NOT_OPEN;
}
return errcode;
}
int
DialogBox::read
(
FILE *fptr
)
{
size_t items; // number of items successfully written
int errcode = ERR_NONE;
if (fptr != NULL)
{
items = fread(dialogData, dataSize, 1, fptr);
if (items != dataSize)
errcode = ERR_FILE_READ;
}
else
{
errcode = ERR_FILE_NOT_OPEN;
}
return errcode;
}