home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_GEN / FACETV.ZIP / TINPEXTD.CPP < prev    next >
C/C++ Source or Header  |  1994-01-04  |  16KB  |  588 lines

  1. /************************************************************************
  2. **
  3. ** @(#)tinpextd.cpp    06/09/93    Chris Ahlstrom
  4. **
  5. ** "Variations on TInputLine in C++", Op. 3.13
  6. **
  7. **    This module provides an abstract base class that inherits
  8. ** capabilities from TInputLine, but then extends it to handle linear
  9. ** mappings of numbers from an internal domain (called "code") to a
  10. ** user-visible domain (called "user").  It also provides the ability
  11. ** to edit numerical fields using the mouse's vertical motion or
  12. ** the up/down arrows.
  13. **
  14. **    However, objects derived from TExtended must provide their
  15. ** own interface by overriding certain TInputLine member functions.
  16. ** See tinput.cpp for some good examples (e.g. TInputInteger).
  17. **
  18. **    Also see tinpmous.cpp for more derived objects of amazing power!
  19. **
  20. *************************************************************************/
  21.  
  22. #define TINPEXTD_cpp
  23.  
  24. #include <conio.h>
  25. #include <dos.h>
  26. #include <math.h>
  27.  
  28. #include "tinpextd.h"
  29.  
  30. extern "C"
  31. {
  32.     #include "tmouse.h"
  33. }
  34.  
  35. __link(RExtended)        // ready it for streaming
  36.  
  37.  
  38. /************************************************************************
  39. ** TExtended::TExtended
  40. **
  41. **    A base class for the numeric "TInput" classes.
  42. **
  43. **    It allows one to specify that linear mappings will take place
  44. ** before and after the data is displayed.
  45. **
  46. **    We include a couple of virtual functions for linear mapping,
  47. ** in case you want to create another numeric class but are too lazy to
  48. ** worry about mapping functions.
  49. **
  50. *************************************************************************/
  51.  
  52. const char * const TExtended::name = "TExtended";
  53.  
  54.  
  55. TExtended::TExtended
  56. (
  57.     const TRect& bounds,        // rectangle for the display
  58.     int cmaxLen,            // the length of the buffer
  59.     const Range& code,            // range of internal representation
  60.     char *formatstring,            // desired display format
  61.     int mapped,                // should remapping be performed?
  62.     const Range& user            // range of user's representation
  63. ) :
  64.     TInputLine    (bounds, cmaxLen),    // initialize the TInputLine parts
  65.  
  66.     useMap    (mapped),        // should internal mappings be use?
  67.     codeMin    (code.minimum),        // lowest internal value allowed
  68.     codeMax    (code.maximum),        // highest internal value allowed
  69.     userMin    (user.minimum),        // lowest "visible" value allowed
  70.     userMax    (user.maximum),        // highest "visible" value allowed
  71.  
  72.     muserMin    (0.0),            // mouse's user-minimum
  73.     muserMax    (0.0),            // mouse's user-maximum
  74.     mouseMin    (24000),        // increase this to slow it down
  75.     mouseMax    (0),            // usually always 0 (ha ha)
  76.     mouseSlope    (0.0),            // calculated in setupMouseMapping()
  77.     kuserMin    (0.0),            // key's user-minimum
  78.     kuserMax    (0.0),            // key's user-maximum
  79.     keyMin    (0),            // usually always 0 (ha ha)
  80.     keyMax    (1000),            // increase this to slow it down
  81.     keySlope    (0.0),            // calculated in setupKeyMapping()
  82.     yMouse    (0),            // current mouse "coordinate"
  83.     yKey    (0),            // current key "coordinate"
  84.     inControl    (0)            // is mouse in private use?
  85. {
  86.     if (!useMap)
  87.     {
  88.     userMin    = codeMin;        // to keep the valid()'s working
  89.     userMax    = codeMax;        // to keep the valid()'s working
  90.     }
  91.  
  92.     /********************************************************************
  93.     ** Prepare the internal mouse mappings for this field.  We could
  94.     ** make them different for key versus mouse if we wanted to drive
  95.     ** the user ape!
  96.     *********************************************************************/
  97.  
  98.     setupMouseMapping(codeMin, codeMax);
  99.     setupKeyMapping(codeMin, codeMax);
  100. }
  101.  
  102.  
  103. /************************************************************************
  104. ** TExtended::mapToUser()
  105. ** TExtended::mapToCode()
  106. **
  107. **    Implements the default, a simple linear mapping using the
  108. ** slope-intercept equation.
  109. **
  110. **    If useMap is set, then we must have matching calls at all times,
  111. ** so the state of the Value can be known.  When we start, doDialog()
  112. ** has just called setData(), and it calls mapToCode(), so that, after
  113. ** construction of the dialog box, all the numeric items are stored in
  114. ** their user-readable format.
  115. **
  116. *************************************************************************/
  117.  
  118. void
  119. TExtended::mapToUser ()
  120. {
  121.     codeValue = getValue();        // use virtual function, get value
  122.     if (useMap)
  123.     {
  124.     double denominator;
  125.     double slope;
  126.  
  127.     denominator = codeMax - codeMin;
  128.     if (denominator == 0.0)
  129.         slope = 1.0;
  130.     else
  131.         slope = (userMax - userMin) / (codeMax - codeMin);
  132.  
  133.     userValue = userMin + (codeValue - codeMin) * slope;
  134.     if (userValue > userMax)
  135.         userValue = userMax;
  136.     else if (userValue < userMin)
  137.         userValue = userMin;
  138.     else if (userValue == -0.0)
  139.         userValue = 0.0;
  140.     }
  141.     else
  142.     {
  143.     userValue = codeValue;
  144.     }
  145.     putValue(userValue);        // put value back in derived object
  146. }
  147.  
  148.  
  149. void
  150. TExtended::mapToCode ()
  151. {
  152.     userValue = getValue();        // use virtual function, get value
  153.     if (useMap)
  154.     {
  155.     double denominator;
  156.     double slope;
  157.  
  158.     denominator = userMax - userMin;
  159.     if (denominator == 0.0)
  160.         slope = 1.0;
  161.     else
  162.         slope = (codeMax - codeMin) / (userMax - userMin);
  163.  
  164.     codeValue = codeMin + (userValue - userMin) * slope;
  165.     if (codeValue > codeMax)
  166.         codeValue = codeMax;
  167.     else if (codeValue < codeMin)
  168.         codeValue = codeMin;
  169.     else if (codeValue == -0.0)
  170.         codeValue = 0.0;
  171.     }
  172.     else
  173.     {
  174.     codeValue = userValue;
  175.     }
  176.     putValue(codeValue);        // put value back in derived object
  177. }
  178.  
  179.  
  180. /************************************************************************
  181. ** TExtended::handleEvent()
  182. **
  183. **    Intercepts the mouse and keystrokes in order to allow some
  184. ** of these events to be used to manipulate the values of fields.
  185. **
  186. *************************************************************************/
  187.  
  188. void
  189. TExtended::handleEvent
  190. (
  191.     TEvent& event
  192. )
  193. {
  194.     /********************************************************************
  195.     ** Handling of the Mouse
  196.     *********************************************************************/
  197.  
  198.     if (event.what == evMouseDown)        // preempt handleEvent()
  199.     {
  200.     if (event.mouse.buttons == RIGHT_MOUSEBUTTON)
  201.     {
  202.         if (!inControl)
  203.         {
  204.         handleMouseControl();        // home-grown loop
  205.         setMousePosition        // restore cursor location
  206.         (
  207.             event.mouse.where.x, event.mouse.where.y
  208.         );
  209.         }
  210.         clearEvent(event);            // don't let TV have it
  211.     }
  212.     }
  213.  
  214.     /********************************************************************
  215.     ** Handling of events for TInputLine
  216.     *********************************************************************/
  217.  
  218.     TInputLine::handleEvent(event);        // handle the TV stuff
  219.  
  220.     /********************************************************************
  221.     ** Handling of the Up and Down arrow keys
  222.     **
  223.     **    At present, there is a problem here for integers, that is not
  224.     ** present for the mouse handler above.  The problem is that we're
  225.     ** re-entering the handleKeyControl() function after every arrow
  226.     ** keystroke.  That routine retrieves the last value as shown on
  227.     ** the screen.  If the mapping is such that the last value doesn't
  228.     ** change appearance, the value is rounded down to the original
  229.     ** value... so we can never change it.
  230.     **
  231.     **    Two solutions:  (1) implement a floating-point accumulator
  232.     ** to store a more precise representation of the integer; (2) make
  233.     ** handleKeyControl() handle the looping, so that another special
  234.     ** keystroke would be required to exit it.  However, then you can't
  235.     ** tab out of the field (as for the mouse code above) without
  236.     ** specifically handling that yourself (yuk)!
  237.     **
  238.     *********************************************************************/
  239.  
  240.     if (event.what == evKeyDown)
  241.     {
  242.     switch (event.keyDown.keyCode)
  243.     {
  244.     case kbUp:
  245.     case kbDown:
  246.  
  247.         handleKeyControl(event.keyDown.keyCode);
  248.         clearEvent(event);
  249.         break;
  250.  
  251.     default:
  252.  
  253.         break;
  254.     }
  255.     }
  256. }
  257.  
  258.  
  259. TStreamableClass
  260. RExtended
  261. (
  262.     TExtended::name,
  263.     TExtended::build,
  264.     __DELTA(TExtended)
  265. );
  266.  
  267.  
  268. /************************************************************************
  269. ** handleMouseControl()
  270. **
  271. **    Handle the Mousestroke (instead of the mouse).
  272. **
  273. **    This routine is entered by pressing the RIGHT mouse button.
  274. ** It is exited when the LEFT mouse button is pressed; if you try
  275. ** to use the RIGHT button for this purpose, well, no user ever
  276. ** lets go quickly enough to avoid hitting the button test!
  277. **
  278. **    Note that when we enter this routine, we use the current
  279. ** Value to properly position the logical cursor.
  280. **
  281. *************************************************************************/
  282.  
  283. void
  284. TExtended::handleMouseControl
  285. (
  286.     void
  287. )
  288. {
  289.     int ypos, ymick;
  290.     double value;
  291.  
  292.     (void) readYmickey();        // establish zero mickey delta
  293.     mapToCode();            // set Value to "code" version
  294.     value = getValue();            // floating "code" version of Value
  295.     yMouse = mouseMapping(value);    // obtain current position
  296.     ypos = yMouse;            // store initial position
  297.     inControl = 1;            // keep mouse in control
  298.     selectAll(True);            // ensure field looks "in use"
  299.     TMouse::hide();            // the mouse cursor confuses us
  300.     while (inControl)
  301.     {
  302.     ymick = readYmickey();        // see if mouse moved any
  303.     if (ymick != 0)            // did it move in y-dimension?
  304.     {
  305.         ypos += ymick;
  306.         ypos = mouseCorrect(ypos);    // make sure it's ok
  307.         if (ypos != yMouse)
  308.         {
  309.         yMouseOld = yMouse;        // save old position
  310.         yMouse = ypos;            // save new position
  311.         }
  312.         value = userMouseMapping(yMouse);    // convert mouse y-value
  313.         putValue(value);            // put back into Value
  314.         updateValue();            // update the screen
  315.         mapToCode();            // convert back to "code"
  316.     }
  317.     if (readMouseButtons() == LEFT_MOUSEBUTTON)
  318.     {
  319.         inControl = 0;
  320.     }
  321.     }
  322.     mapToUser();            // insure it's back to user format
  323.     while (kbhit())            // gobble up any stray keystrokes
  324.     (void) getch();
  325.     TMouse::show();
  326.     selectAll(False);            // unhighlight the field
  327. }
  328.  
  329.  
  330. /************************************************************************
  331. ** mouseCorrect()
  332. **
  333. **    Makes sure the current mouse position is in the correct
  334. ** range, and returns a corrected position if so; otherwise, returns
  335. ** the position unaltered.
  336. **
  337. **    The test isn't quite so simple, because we invert the normal
  338. ** direction of the mouse, so that moving the cursor up increases the
  339. ** number
  340. **
  341. *************************************************************************/
  342.  
  343. int
  344. TExtended::mouseCorrect
  345. (
  346.     int ypos
  347. )
  348. {
  349.     if (mouseMin < mouseMax)
  350.     {
  351.     if (ypos < mouseMin)
  352.         ypos = mouseMin;
  353.     else if (ypos > mouseMax)
  354.         ypos = mouseMax;
  355.     }
  356.     else
  357.     {
  358.     if (ypos > mouseMin)
  359.         ypos = mouseMin;
  360.     else if (ypos < mouseMax)
  361.         ypos = mouseMax;
  362.     }
  363.     return ypos;
  364. }
  365.  
  366.  
  367. /************************************************************************
  368. ** handleKeyControl()
  369. **
  370. **    Handle the keystroke (instead of the mouse).
  371. **
  372. **    For safety, we retrieve the previous value, in case the mouse had
  373. ** modified it.
  374. **
  375. *************************************************************************/
  376.  
  377. void
  378. TExtended::handleKeyControl
  379. (
  380.     int key
  381. )
  382. {
  383.     double value;
  384.  
  385.     mapToCode();            // set Value to "code" version
  386.     value = getValue();            // get the object's Value ("code")
  387.     yKey = keyMapping(value);        // obtain current key level
  388.     yKeyOld = yKey;            // store initial position
  389.     if (key == kbUp)            // up-arrow key
  390.     {
  391.     yKey++;
  392.  
  393.     }
  394.     else if (key == kbDown)        // down-arrow key
  395.     {
  396.     yKey--;
  397.     }
  398.  
  399.     if (yKey < keyMin)
  400.     yKey = keyMin;
  401.     else if (yKey > keyMax)
  402.     yKey = keyMax;
  403.     if (yKey != yKeyOld)
  404.     {
  405.     yKeyOld = yKey;            // save new position
  406.     }
  407.     value = userKeyMapping(yKey);    // get the appropriate user value
  408.     putValue(value);            // convert it to right data-type
  409.     updateValue();            // update the screen ("user" format)
  410. }
  411.  
  412.  
  413. /************************************************************************
  414. ** setupMouseMapping()
  415. **
  416. **    Sets up the above statics for use with mouseMapping() and
  417. ** userMouseMapping() below.
  418. **
  419. *************************************************************************/
  420.  
  421. void
  422. TExtended::setupMouseMapping
  423. (
  424.     double min,
  425.     double max
  426. )
  427. {
  428.     double denominator;
  429.     double slope;
  430.  
  431.     denominator = (double) (mouseMax - mouseMin);
  432.     if (denominator == 0.0)        // if bad mouse range
  433.     slope = 0.0;            // set up so that y = y0
  434.     else
  435.     slope = (max - min) / denominator;
  436.     mouseSlope = slope;
  437.     muserMin = min;
  438.     muserMax = max;
  439. }
  440.  
  441.  
  442. /************************************************************************
  443. ** setupKeyMapping()
  444. **
  445. **    Sets up the above statics for use with keyMapping() and
  446. ** userKeyMapping() below.
  447. **
  448. *************************************************************************/
  449.  
  450. void
  451. TExtended::setupKeyMapping
  452. (
  453.     double min,
  454.     double max
  455. )
  456. {
  457.     double denominator;
  458.     double slope;
  459.  
  460.     denominator = (double) (keyMax - keyMin);
  461.     if (denominator == 0.0)        // if bad key range
  462.     slope = 0.0;            // set up so that y = y0
  463.     else
  464.     slope = (max - min) / denominator;
  465.     keySlope = slope;
  466.     kuserMin = min;
  467.     kuserMax = max;
  468. }
  469.  
  470.  
  471. /************************************************************************
  472. ** userMouseMapping()
  473. **
  474. **    Converts the given mouse value to the user domain.  The value is
  475. ** restricted to the range
  476. **
  477. **        muserMin <= uservalue <= muserMax
  478. **
  479. *************************************************************************/
  480.  
  481. double
  482. TExtended::userMouseMapping
  483. (
  484.     int mousevalue        // current value of mouse y-mickey
  485. )
  486. {
  487.     double uservalue;
  488.  
  489.     uservalue = muserMin + ((double)mousevalue - mouseMin) * mouseSlope;
  490.     if (uservalue > muserMax)
  491.     uservalue = muserMax;
  492.     else if (uservalue < muserMin)
  493.     uservalue = muserMin;
  494.     else if (uservalue == -0.0)
  495.     uservalue = 0.0;
  496.  
  497.     return uservalue;
  498. }
  499.  
  500.  
  501. /************************************************************************
  502. ** userKeyMapping()
  503. **
  504. **    Converts the given key value to the user domain.  The value is
  505. ** restricted to the range
  506. **
  507. **        kuserMin <= uservalue <= kuserMax
  508. **
  509. *************************************************************************/
  510.  
  511. double
  512. TExtended::userKeyMapping
  513. (
  514.     int keyvalue        // current value of key counter
  515. )
  516. {
  517.     double uservalue;
  518.  
  519.     uservalue = kuserMin + ((double)keyvalue - keyMin) * keySlope;
  520.     if (uservalue > kuserMax)
  521.     uservalue = kuserMax;
  522.     else if (uservalue < kuserMin)
  523.     uservalue = kuserMin;
  524.     else if (uservalue == -0.0)
  525.     uservalue = 0.0;
  526.  
  527.     return uservalue;
  528. }
  529.  
  530.  
  531. /************************************************************************
  532. ** mouseMapping()
  533. **
  534. **    Converts the given user value back to the corresponding
  535. ** mouse value to the user domain.  The value is restricted to the range
  536. **
  537. **        mouseMin <= mousevalue <= mouseMax
  538. **
  539. *************************************************************************/
  540.  
  541. int
  542. TExtended::mouseMapping
  543. (
  544.     double uservalue        // value of numeric field
  545. )
  546. {
  547.     double mousevalue;
  548.  
  549.     mousevalue = (mouseSlope != 0.0) ?
  550.     (mouseMin + ((uservalue - muserMin) / mouseSlope)) : uservalue;
  551.     if (mousevalue == -0.0)
  552.     mousevalue = 0.0;
  553.     mousevalue = floor(mousevalue + 0.5);    // round up (cast can't do it)
  554.  
  555.     return mouseCorrect((int) mousevalue);    // make sure it's ok
  556. }
  557.  
  558.  
  559. /************************************************************************
  560. ** keyMapping()
  561. **
  562. **    Converts the given user value back to the corresponding
  563. ** key value to the user domain.  The value is restricted to the range
  564. **
  565. **        keyMin <= keyvalue <= keyMax
  566. **
  567. *************************************************************************/
  568.  
  569. int
  570. TExtended::keyMapping
  571. (
  572.     double uservalue        // value of the numeric field
  573. )
  574. {
  575.     double keyvalue;
  576.  
  577.     keyvalue = (keySlope != 0.0) ?
  578.     (keyMin + ((uservalue - kuserMin) / keySlope)) : uservalue;
  579.     if (keyvalue > keyMax)
  580.     keyvalue = keyMax;
  581.     else if (keyvalue < keyMin)
  582.     keyvalue = keyMin;
  583.     keyvalue = floor(keyvalue + 0.5);        // round up (cast can't do it)
  584.  
  585.     return (int) keyvalue;
  586. }
  587.  
  588.