home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1994 #1
/
monster.zip
/
monster
/
PROG_GEN
/
FACETV.ZIP
/
TINPMOUS.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-01-04
|
18KB
|
752 lines
/************************************************************************
**
** @(#)tinpmous.cpp 06/09/93 Chris Ahlstrom
**
** "Variations on TInputLine in C++", Op. 3.03
**
** This module provides an object that inherits capabilities
** from TInputFloat, and extends it to handle changing values with
** the mouse or up/down arrows.
**
** To implement these functions, we must override handleEvent()
** and maybe some other TInputLine routines.
**
** One important note... for now, I have derived this mouse- and
** key-handling object (TInputFloatControl) from TInputFloat, which is
** itself derived from TExtended, which already has its own rudimentary
** mouse- and key-handling system. So far, they don't seem to interfere,
** but maybe there's a better way to do this.
**
*************************************************************************/
#define TINPMOUS_cpp
#include <conio.h>
#include <dos.h>
#include <math.h>
#include "tinpmous.h"
extern "C"
{
#include "tmouse.h"
}
__link(RInputFloatControl) // ready it for streaming
/************************************************************************
** TInputFloatControl
*************************************************************************/
const char * const TInputFloatControl::name = "TInputFloatControl";
TInputFloatControl::TInputFloatControl
(
const TRect& bounds, // rectangle for Float's input-line
int fmaxLen, // maximum length to use for 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
int typeofmap, // 1=using exponential mapping
int maxmickey, // highest mouse changes we support
int maxkey, // highest key changes we support
CtrlFunction ctrlf // function to use for control
) :
TInputFloat (bounds, fmaxLen, code, formatstring, mapped, user),
mouseKeyMap ((MapType) typeofmap),
mouseMin (maxmickey),
mouseMax (0),
mouseSlope (0.0),
keyMin (0),
keyMax (maxkey),
keySlope (0.0),
userMin (user.minimum),
userMax (user.maximum),
userControl (ctrlf),
yMouse (0),
yKey (0),
inControl (0)
{
/********************************************************
** Prepare the internal mappings for this field.
*********************************************************/
setupMouseMapping();
setupKeyMapping();
}
void
TInputFloatControl::handleEvent
(
TEvent& event
)
{
/********************************************************************
** Handling of the Up and Down arrow keys
**
** We don't call clearEvent() unless the keystroke is one we need to
** handle.
*********************************************************************/
if (event.what == evMouseDown) // preempt handleEvent()
{
if (event.mouse.buttons == RIGHT_MOUSE_BUTTON)
{
if (!inControl)
{
(void) handleMouseControl(); // home-grown loop
setMousePosition // restore cursor location
(
event.mouse.where.x, event.mouse.where.y
);
}
clearEvent(event); // don't let TV have it
}
}
/********************************************************************
** Normal event-handling
**
** Although TInputFloatControl is derived from TInputFloat, we
** do not want to call TInputFloat::handleEvent(event), because
** it will intercept the up/down arrows and prevent them from
** attempting to execute the userControl() function.
*********************************************************************/
TInputLine::handleEvent(event); // handle the TV stuff
/********************************************************************
** Handling of the Mouse
**
** We must first handle the right-click that starts the allowing
** of the mouse to control the number. Since TInputLine's
** handleEvent() clears all evMouseDown events, we have to
** intercept it here, to see if it's the right-button click
** we want to handle.
**
** Then we enter a closed loop that takes total control of the
** mouse until the left mouse button is pressed. Is this the
** best way? I don't know.
*********************************************************************/
if (event.what == evKeyDown)
{
int goodkey;
switch (event.keyDown.keyCode)
{
case kbUp:
goodkey = 1;
break;
case kbDown:
goodkey = 1;
break;
default:
goodkey = 0;
break;
}
if (goodkey)
{
Value = handleKeyControl(event.keyDown.keyCode);
setData(&Value);
if (userControl != NULL)
{
(void) (*userControl)(Value); // implement knob twist
}
clearEvent(event);
}
}
}
TStreamableClass
RInputFloatControl
(
TInputFloatControl::name,
TInputFloatControl::build,
__DELTA(TInputFloatControl)
);
/************************************************************************
** 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.
**
*************************************************************************/
double
TInputFloatControl::handleMouseControl
(
void
)
{
int ypos, ymick;
(void) readYmickey(); // establish zero mickey delta
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
setData(&Value); // update the screen
if (userControl != NULL) // a function supplied?
{
(void) (*userControl)(Value); // implement knob twist
}
}
if (readMouseButtons() == LEFT_MOUSE_BUTTON)
{
inControl = 0;
}
}
while (kbhit()) // gobble up any stray keystrokes
(void) getch();
TMouse::show();
selectAll(False); // unhighlight the field
return Value;
}
/************************************************************************
** 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
TInputFloatControl::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.
**
*************************************************************************/
double
TInputFloatControl::handleKeyControl
(
int key
)
{
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
}
return userKeyMapping(yKey); // get the appropriate user value
}
/************************************************************************
** Mouse Mapping
**
** To save time (by passing parameters just once and making the
** slope calculation just once), this routine logs the parameters of
** the linear mapping conversion *and* the logarithmic (exponential)
** mapping conversion.
**
** Note that we could reprogram the mouse itself, using the
** mouse mapping profile function. (But maybe that affects only
** the acceleration.)
**
**
** LINEAR EXPONENTIAL
** | |
** userMax-| -| *
** | | *
** | * | *
** | * | *
** | * | *
** | * | *
** | * | *
** | * | *
** | * | *
** userMin-| * -| * *
** | |
** | |
** | |
** -------------------------- -------------------------
** | | | |
** mouseMin mouseMax mouseMin mouseMax
**
**
** LINEAR:
**
** This mapping is the simple slope-intercept form of
** a linear equation. It is created by using the fact that
** the extrema of the line form a similar triangle to that formed
** by the minimum point of the line and the desired data point.
**
** x = mouse values (x0 = mouseMin, x1 = mouseMax)
** y = user values (y0 = userMin, y1 = userMax)
** m = slope (mouseSlope)
**
** y1 - y0
** m = ---------
** x1 - x0
**
** y = y0 + m(x - x0)
**
** EXPONENTIAL:
**
** To make this mapping, we simply transform y into
** a logarithmic variable by the transformation
**
** z = ln y (ln is the natural logarithm)
**
** Then, we form the linear equation above using z instead of y.
** This gives us:
**
** z1 - z0
** z = z0 + --------- (x - x0)
** x1 - x0
**
** which is really
**
** ln(y1) - ln(y0)
** ln y = ln y0 + ----------------- (x - x0)
** x1 - x0
**
** We raise e to both size, which gives us x and y defined as
** above, but with a different m:
**
** ln(y1) - ln(y0)
** m = -----------------
** x1 - x0
**
** m(x - x0)
** y = y0 e
**
**
*************************************************************************/
/************************************************************************
** setupMouseMapping()
**
** Sets up the above statics for use with mouseMapping() and
** userMouseMapping() below.
**
** Note that, if the programmer supplies a 0 minimum value and
** asks for an exponential mapping, we force the minimum value to
** create a 40 dB range in the user's variable.
**
*************************************************************************/
void
TInputFloatControl::setupMouseMapping
(
void
)
{
double denominator;
double slope;
denominator = (double) (mouseMax - mouseMin);
switch (mouseKeyMap)
{
case LINEAR:
if (denominator == 0.0) // if bad mouse range
slope = 0.0; // set up so that y = y0
else
slope = (userMax - userMin) / denominator;
break;
case EXPONENTIAL:
if (userMin <= 0.0) // bad minimum value
{
userMin = userMax / 100.0; // make it a 40 dB range
}
denominator = (double) (mouseMax - mouseMin);
if (denominator == 0.0) // if bad mouse range
slope = 0.0; // set up so that y = y0
else
slope = (log(userMax) - log(userMin)) / denominator;
break;
}
mouseSlope = slope;
}
/************************************************************************
** setupKeyMapping()
**
** Sets up the above statics for use with keyMapping() and
** userKeyMapping() below.
**
** Note that, if the programmer supplies a 0 minimum value and
** asks for an exponential mapping, we force the minimum value to
** create a 40 dB range in the user's variable.
**
*************************************************************************/
void
TInputFloatControl::setupKeyMapping
(
void
)
{
double denominator;
double slope;
denominator = (double) (keyMax - keyMin);
switch (mouseKeyMap)
{
case LINEAR:
if (denominator == 0.0) // if bad key range
slope = 0.0; // set up so that y = y0
else
slope = (userMax - userMin) / denominator;
break;
case EXPONENTIAL:
if (userMin <= 0.0) // bad minimum value
{
userMin = userMax / 100.0; // make it a 40 dB range
}
denominator = (double) (keyMax - keyMin);
if (denominator == 0.0) // if bad key range
slope = 0.0; // set up so that y = y0
else
slope = (log(userMax) - log(userMin)) / denominator;
break;
}
keySlope = slope;
}
/************************************************************************
** userMouseMapping()
**
** Converts the given mouse value (usually in the range of
** 0 to 392) to the user domain. If any screwups occur, the value
** is restricted to the range
**
** userMin <= userMapping <= userMax
**
*************************************************************************/
double
TInputFloatControl::userMouseMapping
(
int mousevalue // current value of mouse y-mickey
)
{
double uservalue;
switch (mouseKeyMap)
{
case LINEAR:
uservalue = userMouseLinear(mousevalue);
break;
case EXPONENTIAL:
uservalue = userMouseExp(mousevalue);
break;
}
if (uservalue > userMax)
uservalue = userMax;
else if (uservalue < userMin)
uservalue = userMin;
else if (uservalue == -0.0)
uservalue = 0.0;
return uservalue;
}
/************************************************************************
** userKeyMapping()
**
** Converts the given mouse value (usually in the range of
** 0 to 392) to the user domain. If any screwups occur, the value
** is restricted to the range
**
** userMin <= userMapping <= userMax
**
*************************************************************************/
double
TInputFloatControl::userKeyMapping
(
int keyvalue // current value of key counter
)
{
double uservalue;
switch (mouseKeyMap)
{
case LINEAR:
uservalue = userKeyLinear(keyvalue);
break;
case EXPONENTIAL:
uservalue = userKeyExp(keyvalue);
break;
}
if (uservalue > userMax)
uservalue = userMax;
else if (uservalue < userMin)
uservalue = userMin;
else if (uservalue == -0.0)
uservalue = 0.0;
return uservalue;
}
/************************************************************************
** mouseMapping()
**
** Converts the given user value back to the corresponding
** mouse value (usually in the range of 0 to 392) to the user domain.
** If any screwups occur, the value is restricted to the range
**
** mouseMin <= mouseKeyMap <= mouseMax
**
*************************************************************************/
int
TInputFloatControl::mouseMapping
(
double uservalue // value of numeric field
)
{
double mousevalue;
switch (mouseKeyMap)
{
case LINEAR:
mousevalue = mouseLinear(uservalue);
break;
case EXPONENTIAL:
mousevalue = mouseExp(uservalue);
break;
}
return mouseCorrect((int) mousevalue); // make sure it's ok
}
/************************************************************************
** keyMapping()
**
** Converts the given user value back to the corresponding
** key value (usually in the range of 0 to 392) to the user domain.
** If any screwups occur, the value is restricted to the range
**
** keyMin <= mouseKeyMapping <= keyMax
**
*************************************************************************/
int
TInputFloatControl::keyMapping
(
double uservalue // value of the numeric field
)
{
double keyvalue;
switch (mouseKeyMap)
{
case LINEAR:
keyvalue = keyLinear(uservalue);
break;
case EXPONENTIAL:
keyvalue = keyExp(uservalue);
break;
}
if (keyvalue > keyMax)
keyvalue = keyMax;
else if (keyvalue < keyMin)
keyvalue = keyMin;
return (int) keyvalue;
}
/************************************************************************
** Various mapping formulas
**
** I'd like to just have these be functions associated with
** objects created in setupMouseMapping() and setupKeyMapping(),
** and thus called indirectly. For now, I don't want to bother with
** such elegance.
**
*************************************************************************/
double
TInputFloatControl::userMouseLinear
(
double mousevalue
)
{
return (userMin + (mousevalue - mouseMin) * mouseSlope);
}
double
TInputFloatControl::userMouseExp
(
double mousevalue
)
{
return (userMin * exp((mousevalue - mouseMin) * mouseSlope));
}
double
TInputFloatControl::userKeyLinear
(
double keyvalue
)
{
return (userMin + (keyvalue - keyMin) * keySlope);
}
double
TInputFloatControl::userKeyExp
(
double keyvalue
)
{
return (userMin * exp((keyvalue - keyMin) * keySlope));
}
double
TInputFloatControl::mouseLinear
(
double uservalue
)
{
return (mouseSlope != 0.0) ?
(mouseMin + ((uservalue - userMin) / mouseSlope)) : uservalue;
}
double
TInputFloatControl::mouseExp
(
double uservalue
)
{
return (mouseSlope != 0.0) ?
(mouseMin + (log(uservalue/userMin) / mouseSlope)) : uservalue;
}
double
TInputFloatControl::keyLinear
(
double uservalue
)
{
return (keySlope != 0.0) ?
(keyMin + ((uservalue - userMin) / keySlope)) : uservalue;
}
double
TInputFloatControl::keyExp
(
double uservalue
)
{
return (keySlope != 0.0) ?
(keyMin + (log(uservalue/userMin) / keySlope)) : uservalue;
}