home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
PROG_GEN
/
FACETV.ZIP
/
TINPEXTD.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-04
|
16KB
|
588 lines
/************************************************************************
**
** @(#)tinpextd.cpp 06/09/93 Chris Ahlstrom
**
** "Variations on TInputLine in C++", Op. 3.13
**
** This module provides an abstract base class that inherits
** capabilities from TInputLine, but then extends it to handle linear
** mappings of numbers from an internal domain (called "code") to a
** user-visible domain (called "user"). It also provides the ability
** to edit numerical fields using the mouse's vertical motion or
** the up/down arrows.
**
** However, objects derived from TExtended must provide their
** own interface by overriding certain TInputLine member functions.
** See tinput.cpp for some good examples (e.g. TInputInteger).
**
** Also see tinpmous.cpp for more derived objects of amazing power!
**
*************************************************************************/
#define TINPEXTD_cpp
#include <conio.h>
#include <dos.h>
#include <math.h>
#include "tinpextd.h"
extern "C"
{
#include "tmouse.h"
}
__link(RExtended) // ready it for streaming
/************************************************************************
** TExtended::TExtended
**
** A base class for the numeric "TInput" classes.
**
** It allows one to specify that linear mappings will take place
** before and after the data is displayed.
**
** We include a couple of virtual functions for linear mapping,
** in case you want to create another numeric class but are too lazy to
** worry about mapping functions.
**
*************************************************************************/
const char * const TExtended::name = "TExtended";
TExtended::TExtended
(
const TRect& bounds, // rectangle for the display
int cmaxLen, // the length of the buffer
const Range& code, // range of internal representation
char *formatstring, // desired display format
int mapped, // should remapping be performed?
const Range& user // range of user's representation
) :
TInputLine (bounds, cmaxLen), // initialize the TInputLine parts
useMap (mapped), // should internal mappings be use?
codeMin (code.minimum), // lowest internal value allowed
codeMax (code.maximum), // highest internal value allowed
userMin (user.minimum), // lowest "visible" value allowed
userMax (user.maximum), // highest "visible" value allowed
muserMin (0.0), // mouse's user-minimum
muserMax (0.0), // mouse's user-maximum
mouseMin (24000), // increase this to slow it down
mouseMax (0), // usually always 0 (ha ha)
mouseSlope (0.0), // calculated in setupMouseMapping()
kuserMin (0.0), // key's user-minimum
kuserMax (0.0), // key's user-maximum
keyMin (0), // usually always 0 (ha ha)
keyMax (1000), // increase this to slow it down
keySlope (0.0), // calculated in setupKeyMapping()
yMouse (0), // current mouse "coordinate"
yKey (0), // current key "coordinate"
inControl (0) // is mouse in private use?
{
if (!useMap)
{
userMin = codeMin; // to keep the valid()'s working
userMax = codeMax; // to keep the valid()'s working
}
/********************************************************************
** Prepare the internal mouse mappings for this field. We could
** make them different for key versus mouse if we wanted to drive
** the user ape!
*********************************************************************/
setupMouseMapping(codeMin, codeMax);
setupKeyMapping(codeMin, codeMax);
}
/************************************************************************
** TExtended::mapToUser()
** TExtended::mapToCode()
**
** Implements the default, a simple linear mapping using the
** slope-intercept equation.
**
** If useMap is set, then we must have matching calls at all times,
** so the state of the Value can be known. When we start, doDialog()
** has just called setData(), and it calls mapToCode(), so that, after
** construction of the dialog box, all the numeric items are stored in
** their user-readable format.
**
*************************************************************************/
void
TExtended::mapToUser ()
{
codeValue = getValue(); // use virtual function, get value
if (useMap)
{
double denominator;
double slope;
denominator = codeMax - codeMin;
if (denominator == 0.0)
slope = 1.0;
else
slope = (userMax - userMin) / (codeMax - codeMin);
userValue = userMin + (codeValue - codeMin) * slope;
if (userValue > userMax)
userValue = userMax;
else if (userValue < userMin)
userValue = userMin;
else if (userValue == -0.0)
userValue = 0.0;
}
else
{
userValue = codeValue;
}
putValue(userValue); // put value back in derived object
}
void
TExtended::mapToCode ()
{
userValue = getValue(); // use virtual function, get value
if (useMap)
{
double denominator;
double slope;
denominator = userMax - userMin;
if (denominator == 0.0)
slope = 1.0;
else
slope = (codeMax - codeMin) / (userMax - userMin);
codeValue = codeMin + (userValue - userMin) * slope;
if (codeValue > codeMax)
codeValue = codeMax;
else if (codeValue < codeMin)
codeValue = codeMin;
else if (codeValue == -0.0)
codeValue = 0.0;
}
else
{
codeValue = userValue;
}
putValue(codeValue); // put value back in derived object
}
/************************************************************************
** TExtended::handleEvent()
**
** Intercepts the mouse and keystrokes in order to allow some
** of these events to be used to manipulate the values of fields.
**
*************************************************************************/
void
TExtended::handleEvent
(
TEvent& event
)
{
/********************************************************************
** Handling of the Mouse
*********************************************************************/
if (event.what == evMouseDown) // preempt handleEvent()
{
if (event.mouse.buttons == RIGHT_MOUSEBUTTON)
{
if (!inControl)
{
handleMouseControl(); // home-grown loop
setMousePosition // restore cursor location
(
event.mouse.where.x, event.mouse.where.y
);
}
clearEvent(event); // don't let TV have it
}
}
/********************************************************************
** Handling of events for TInputLine
*********************************************************************/
TInputLine::handleEvent(event); // handle the TV stuff
/********************************************************************
** Handling of the Up and Down arrow keys
**
** At present, there is a problem here for integers, that is not
** present for the mouse handler above. The problem is that we're
** re-entering the handleKeyControl() function after every arrow
** keystroke. That routine retrieves the last value as shown on
** the screen. If the mapping is such that the last value doesn't
** change appearance, the value is rounded down to the original
** value... so we can never change it.
**
** Two solutions: (1) implement a floating-point accumulator
** to store a more precise representation of the integer; (2) make
** handleKeyControl() handle the looping, so that another special
** keystroke would be required to exit it. However, then you can't
** tab out of the field (as for the mouse code above) without
** specifically handling that yourself (yuk)!
**
*********************************************************************/
if (event.what == evKeyDown)
{
switch (event.keyDown.keyCode)
{
case kbUp:
case kbDown:
handleKeyControl(event.keyDown.keyCode);
clearEvent(event);
break;
default:
break;
}
}
}
TStreamableClass
RExtended
(
TExtended::name,
TExtended::build,
__DELTA(TExtended)
);
/************************************************************************
** handleMouseControl()
**
** Handle the Mousestroke (instead of the mouse).
**
** This routine is entered by pressing the RIGHT mouse button.
** It is exited when the LEFT mouse button is pressed; if you try
** to use the RIGHT button for this purpose, well, no user ever
** lets go quickly enough to avoid hitting the button test!
**
** Note that when we enter this routine, we use the current
** Value to properly position the logical cursor.
**
*************************************************************************/
void
TExtended::handleMouseControl
(
void
)
{
int ypos, ymick;
double value;
(void) readYmickey(); // establish zero mickey delta
mapToCode(); // set Value to "code" version
value = getValue(); // floating "code" version of Value
yMouse = mouseMapping(value); // obtain current position
ypos = yMouse; // store initial position
inControl = 1; // keep mouse in control
selectAll(True); // ensure field looks "in use"
TMouse::hide(); // the mouse cursor confuses us
while (inControl)
{
ymick = readYmickey(); // see if mouse moved any
if (ymick != 0) // did it move in y-dimension?
{
ypos += ymick;
ypos = mouseCorrect(ypos); // make sure it's ok
if (ypos != yMouse)
{
yMouseOld = yMouse; // save old position
yMouse = ypos; // save new position
}
value = userMouseMapping(yMouse); // convert mouse y-value
putValue(value); // put back into Value
updateValue(); // update the screen
mapToCode(); // convert back to "code"
}
if (readMouseButtons() == LEFT_MOUSEBUTTON)
{
inControl = 0;
}
}
mapToUser(); // insure it's back to user format
while (kbhit()) // gobble up any stray keystrokes
(void) getch();
TMouse::show();
selectAll(False); // unhighlight the field
}
/************************************************************************
** mouseCorrect()
**
** Makes sure the current mouse position is in the correct
** range, and returns a corrected position if so; otherwise, returns
** the position unaltered.
**
** The test isn't quite so simple, because we invert the normal
** direction of the mouse, so that moving the cursor up increases the
** number
**
*************************************************************************/
int
TExtended::mouseCorrect
(
int ypos
)
{
if (mouseMin < mouseMax)
{
if (ypos < mouseMin)
ypos = mouseMin;
else if (ypos > mouseMax)
ypos = mouseMax;
}
else
{
if (ypos > mouseMin)
ypos = mouseMin;
else if (ypos < mouseMax)
ypos = mouseMax;
}
return ypos;
}
/************************************************************************
** handleKeyControl()
**
** Handle the keystroke (instead of the mouse).
**
** For safety, we retrieve the previous value, in case the mouse had
** modified it.
**
*************************************************************************/
void
TExtended::handleKeyControl
(
int key
)
{
double value;
mapToCode(); // set Value to "code" version
value = getValue(); // get the object's Value ("code")
yKey = keyMapping(value); // obtain current key level
yKeyOld = yKey; // store initial position
if (key == kbUp) // up-arrow key
{
yKey++;
}
else if (key == kbDown) // down-arrow key
{
yKey--;
}
if (yKey < keyMin)
yKey = keyMin;
else if (yKey > keyMax)
yKey = keyMax;
if (yKey != yKeyOld)
{
yKeyOld = yKey; // save new position
}
value = userKeyMapping(yKey); // get the appropriate user value
putValue(value); // convert it to right data-type
updateValue(); // update the screen ("user" format)
}
/************************************************************************
** setupMouseMapping()
**
** Sets up the above statics for use with mouseMapping() and
** userMouseMapping() below.
**
*************************************************************************/
void
TExtended::setupMouseMapping
(
double min,
double max
)
{
double denominator;
double slope;
denominator = (double) (mouseMax - mouseMin);
if (denominator == 0.0) // if bad mouse range
slope = 0.0; // set up so that y = y0
else
slope = (max - min) / denominator;
mouseSlope = slope;
muserMin = min;
muserMax = max;
}
/************************************************************************
** setupKeyMapping()
**
** Sets up the above statics for use with keyMapping() and
** userKeyMapping() below.
**
*************************************************************************/
void
TExtended::setupKeyMapping
(
double min,
double max
)
{
double denominator;
double slope;
denominator = (double) (keyMax - keyMin);
if (denominator == 0.0) // if bad key range
slope = 0.0; // set up so that y = y0
else
slope = (max - min) / denominator;
keySlope = slope;
kuserMin = min;
kuserMax = max;
}
/************************************************************************
** userMouseMapping()
**
** Converts the given mouse value to the user domain. The value is
** restricted to the range
**
** muserMin <= uservalue <= muserMax
**
*************************************************************************/
double
TExtended::userMouseMapping
(
int mousevalue // current value of mouse y-mickey
)
{
double uservalue;
uservalue = muserMin + ((double)mousevalue - mouseMin) * mouseSlope;
if (uservalue > muserMax)
uservalue = muserMax;
else if (uservalue < muserMin)
uservalue = muserMin;
else if (uservalue == -0.0)
uservalue = 0.0;
return uservalue;
}
/************************************************************************
** userKeyMapping()
**
** Converts the given key value to the user domain. The value is
** restricted to the range
**
** kuserMin <= uservalue <= kuserMax
**
*************************************************************************/
double
TExtended::userKeyMapping
(
int keyvalue // current value of key counter
)
{
double uservalue;
uservalue = kuserMin + ((double)keyvalue - keyMin) * keySlope;
if (uservalue > kuserMax)
uservalue = kuserMax;
else if (uservalue < kuserMin)
uservalue = kuserMin;
else if (uservalue == -0.0)
uservalue = 0.0;
return uservalue;
}
/************************************************************************
** mouseMapping()
**
** Converts the given user value back to the corresponding
** mouse value to the user domain. The value is restricted to the range
**
** mouseMin <= mousevalue <= mouseMax
**
*************************************************************************/
int
TExtended::mouseMapping
(
double uservalue // value of numeric field
)
{
double mousevalue;
mousevalue = (mouseSlope != 0.0) ?
(mouseMin + ((uservalue - muserMin) / mouseSlope)) : uservalue;
if (mousevalue == -0.0)
mousevalue = 0.0;
mousevalue = floor(mousevalue + 0.5); // round up (cast can't do it)
return mouseCorrect((int) mousevalue); // make sure it's ok
}
/************************************************************************
** keyMapping()
**
** Converts the given user value back to the corresponding
** key value to the user domain. The value is restricted to the range
**
** keyMin <= keyvalue <= keyMax
**
*************************************************************************/
int
TExtended::keyMapping
(
double uservalue // value of the numeric field
)
{
double keyvalue;
keyvalue = (keySlope != 0.0) ?
(keyMin + ((uservalue - kuserMin) / keySlope)) : uservalue;
if (keyvalue > keyMax)
keyvalue = keyMax;
else if (keyvalue < keyMin)
keyvalue = keyMin;
keyvalue = floor(keyvalue + 0.5); // round up (cast can't do it)
return (int) keyvalue;
}