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

  1. /************************************************************************
  2. **
  3. ** @(#)respintr.cpp    01/04/94    Chris Ahlstrom
  4. **
  5. ** C++ version, requires Turbo Vision by Borland
  6. **
  7. **    This module interfaces C++ with the standard FACE response
  8. ** light code (in FACE_TV.LIB), and with code for handling various kinds
  9. ** of button devices.
  10. **
  11. **    This module has functions very similar to those of
  12. ** response.cpp, but this module only handles mouse events for
  13. ** a response box implemented in a window.
  14. **
  15. **    This version of the "light()" function is based on the
  16. ** implementation of Fan-Gang Zeng (but now uses text to draw the
  17. ** square lights).  It is similar to, but quite a bit more organized
  18. ** and safe than, the code in the MODAPP program's version of
  19. ** respwind.cpp/h.
  20. **
  21. **    The procedure displays the following layout (note that, at
  22. ** present, display of the text strings is not implemented):
  23. **
  24. **
  25. **     ---------                 ---------
  26. **    |  READY  |                | ANSWER  |
  27. **     ---------                 ---------
  28. **     ---------                 ---------
  29. **    |         |                |         |
  30. **    |         |                |         |
  31. **    |         |                |         |
  32. **     ---------                 ---------
  33. **
  34. **         ----------         ----------
  35. **        |Interval 1|        |Interval 2|
  36. **         ----------         ----------
  37. **         ----------         ----------
  38. **        |          |        |          |
  39. **        |          |        |          |
  40. **        |          |        |          |
  41. **         ----------         ----------
  42. **
  43. **
  44. **         ----------         ----------
  45. **        |Response 1|        |Response 2|
  46. **         ----------         ----------
  47. **         ----------         ----------
  48. **        |          |        |          |
  49. **        |          |        |          |
  50. **        |          |        |          |
  51. **         ----------         ----------
  52. **
  53. **
  54. **    This layout is generated in three steps:
  55. **
  56. **        1.  The words are displayed, with the RespWordAttribute,
  57. **        using a text display, at the start of the experiment.
  58. **        A call to "resp_setup()" does this.
  59. **
  60. **        2.  All lights are turned on using a call to light().
  61. **
  62. **        3.  The various sequences of lights are displayed,
  63. **        using calls to light().
  64. **
  65. **    The lights are mapped onto a byte, as shown in the CONST
  66. ** section.  Where ever a bit is set, the corresponding "light"
  67. ** gets turned on.  Some convenient labels are added, too, to
  68. ** represent combinations of lights.
  69. **
  70. *************************************************************************/
  71.  
  72. #define RESPINTR_cpp
  73.  
  74. #include <stdio.h>        // standard i/o
  75. #include <conio.h>        // console i/o
  76. #include <ctype.h>        // character-handling functions
  77. #include <graphics.h>        // Borland graphics library
  78.  
  79.  
  80. #include "respintr.h"        // ResponseInterior class
  81. #include "respvga.h"        // VGA response box parameters
  82.  
  83. extern "C"                // avoid name-mangling
  84. {
  85.     #include "math_aux.h"        // simple math like iround()
  86.     #include "textlog.h"        // getScreenModes()
  87. }
  88.  
  89.  
  90.  
  91. /************************************************************************
  92. ** ResponseInterior constructor and destructor.
  93. **
  94. **    Note that, since we allocate a semi-permanent textBox, it
  95. ** might prove wise to add an assignment operator and copy constructor.
  96. **
  97. *************************************************************************/
  98.  
  99. ResponseInterior::ResponseInterior
  100. (
  101.     ResponseBox& virtualbox,        // light-coordinates in virtual units
  102.     const TRect& bounds            // size of the window
  103. ) :
  104.     TView(bounds)
  105. {
  106.     graphBox    = &virtualbox;            // a global structure
  107.     textBox    = new ResponseBox;        // temporary structure
  108.     if (textBox)
  109.     {
  110.     textBox->background_color    = graphBox->background_color;
  111.     textBox->back_fillstyle        = graphBox->back_fillstyle;
  112.     }
  113.  
  114.     //respBackground = graphBox->background_color;
  115.     //screen = TURBO_VISION_WINDOW;
  116.  
  117.     Screen *sc = new Screen;            // allocate a struct
  118.     if (sc)
  119.     {
  120.     (void) getScreenModes(sc);        // obtain many parameters
  121.     respRows    = sc->screen_rows;    // store number of text rows
  122.     respColumns    = sc->screen_columns;    // store number of text columns
  123.     respPixelX    = sc->screen_gmax_x;    // store pixel depth
  124.     respPixelY    = sc->screen_gmax_y;    // store pixel width
  125.     delete sc;                // get rid of struct
  126.     }
  127.  
  128.     options = options | ofFramed;        // frame the box
  129.     //options = options | ofPostProcess;    // accept leftover events
  130.     growMode = gfGrowHiX | gfGrowHiY;        // size same as window's
  131.  
  132.     textBox->response_1.status      = RESP_ON;
  133.     textBox->response_2.status      = RESP_ON;
  134.     textBox->ready_light.status      = RESP_ON;
  135.     textBox->answer_light.status  = RESP_ON;
  136.     textBox->interval_1.status      = RESP_ON;
  137.     textBox->interval_2.status      = RESP_ON;
  138.     textBox->failure_light.status = RESP_ON;
  139. }
  140.  
  141. ResponseInterior::~ResponseInterior ()
  142. {
  143.     if (textBox)
  144.     delete textBox;
  145. }
  146.  
  147.  
  148. /************************************************************************
  149. ** boxAspectConvert()
  150. **
  151. **    Generates the proper coordinates to specify the layout
  152. ** of a response box, given the type of response box settings in use
  153. ** for the current graphics monitor.
  154. **
  155. **    In addition, the coordinates are scaled to fit inside a box
  156. ** of a particular size (in units of lines and characters).
  157. **
  158. **    This routine is called in the draw() routine, which is overridden
  159. ** here.  By calling it there, the size of the lights will somewhat
  160. ** follow the size of the box, if the user resizes it.
  161. **
  162. *************************************************************************/
  163.  
  164. void
  165. ResponseInterior::boxAspectConvert
  166. (
  167.     TRect *wbox            // window box into which to fit it all
  168. )
  169. {
  170.     lightAspectConvert(wbox, graphBox->response_1,   textBox->response_1);
  171.     lightAspectConvert(wbox, graphBox->response_2,   textBox->response_2);
  172.     lightAspectConvert(wbox, graphBox->ready_light,  textBox->ready_light);
  173.     lightAspectConvert(wbox, graphBox->answer_light, textBox->answer_light);
  174.     lightAspectConvert(wbox, graphBox->interval_1,   textBox->interval_1);
  175.     lightAspectConvert(wbox, graphBox->interval_2,   textBox->interval_2);
  176.  
  177.     textAspectConvert(wbox, graphBox->failure_light, textBox->failure_light);
  178. }
  179.  
  180.  
  181. #if defined(__BORLANDC__)
  182. #pragma warn -sig        // { ignore double-to-int warning
  183. #endif
  184.  
  185. /************************************************************************
  186. ** lightAspectConvert()
  187. **
  188. **    Determines the coordinates of a response light relative to
  189. ** the response-box window in which it resides.  All coordinates,
  190. ** whether in graphics modes or text modes, are converted to text-
  191. ** character cells.
  192. **
  193. **    Also copies some other fields from the source (virtual) light.
  194. **
  195. **    Guide to conversions.  The "screen_" values are as per
  196. ** "screen.c" and "screen.h" in the FACE library.
  197. **
  198. **  1.  Converting virtual coordinates to characters:
  199. **
  200. **    a.  screen_x = (virtual_x / VIRTmax) * screen_columns
  201. **    b.  screen_y = (virtual_y / VIRTmax) * screen_rows
  202. **
  203. **  2.  Converting virtual coordinates to pixels:
  204. **
  205. **    a.  screen_x = (virtual_x / VIRTmax) * screen_gmax_x
  206. **    b.  screen_y = (virtual_y / VIRTmax) * screen_gmax_y
  207. **
  208. **    This routine doesn't handle graphics modes yet, or at
  209. ** least, I haven't tested it.
  210. **        
  211. *************************************************************************/
  212.  
  213. void
  214. ResponseInterior::lightAspectConvert
  215. (
  216.     TRect *wbox,        // window box into which the light goes
  217.     ResponseLight& source,    // pixel-coordinates of the light
  218.     ResponseLight& text        // text-coordinate-offsets of the light
  219. )
  220. {
  221.     int va, vb;
  222.     double maxx;
  223.     double maxy;
  224.     double boxx = (double) (wbox->b.x - wbox->a.x);
  225.     double boxy = (double) (wbox->b.y - wbox->a.y);
  226.  
  227.     text.fillstyle = source.fillstyle;        // style of background
  228.     text.color        = source.color;        // color of response light
  229.  
  230.     if (respPixelX == 0 || respPixelY == 0)
  231.     {                        // we're in text mode
  232.     maxx = (boxx / VIRTmax);
  233.     maxy = (boxy / VIRTmax);
  234.     }
  235.     else
  236.     {                        // we're in graphics mode
  237.     maxx = (respPixelX+1) / VIRTmax / boxx;
  238.     maxy = (respPixelY+1) / VIRTmax / boxy;
  239.     }
  240.  
  241.     va = iround(source.x0 * maxx);
  242.     vb = iround(source.x1 * maxx);
  243.     if ((vb-va) <= 0)                // keep it > 0
  244.     vb = va + 1;
  245.     text.x0 = va;
  246.     text.x1 = vb;
  247.  
  248.     va = iround(source.y0 * maxy);
  249.     vb = iround(source.y1 * maxy);
  250.     if ((vb-va) <= 0)                // keep it > 0
  251.     vb = va + 1;
  252.     text.y0 = va;
  253.     text.y1 = vb;
  254. }
  255.  
  256.  
  257. /************************************************************************
  258. ** textAspectConvert()
  259. **
  260. **    Once the above works, fix this to suit.
  261. **
  262. *************************************************************************/
  263.  
  264. void
  265. ResponseInterior::textAspectConvert
  266. (
  267.     TRect *wbox,        // window box into which the light goes
  268.     ResponseText& light,    // pixel-coordinates of the light
  269.     ResponseText& text        // text-coordinate-offsets of the light
  270. )
  271. {
  272.     double maxx = (double) (respPixelX + 1);
  273.     double maxy = (double) (respPixelY + 1);
  274.     double boxx = (double) (wbox->b.x - wbox->a.x);
  275.     double boxy = (double) (wbox->b.y - wbox->a.y);
  276.  
  277.     if (maxx <= 0.0 || maxy <= 0.0)
  278.     {
  279.     maxx = 1.0;
  280.     maxy = 1.0;
  281.     }
  282.  
  283.     text.x0 = iround((light.x0 / maxx) * boxx);
  284.     text.y0 = iround((light.y0 / maxy) * boxy);
  285. }
  286.  
  287. #if defined(__BORLANDC__)
  288. #pragma warn .sig        // } put warning back to original state
  289. #endif
  290.  
  291.  
  292. /************************************************************************
  293. ** draw() override
  294. *************************************************************************/
  295.  
  296. void
  297. ResponseInterior::draw()
  298. {
  299. //    uchar wcolor = (uchar) getColor(textBox->background_color);
  300.     uchar wcolor = (uchar) textBox->background_color;
  301.  
  302.     TRect wbox = getExtent();        // size of the window
  303.     wbox.grow(-1, -1);                // make it fit in window
  304.     boxAspectConvert(&wbox);            // get new (maybe) sizes
  305.  
  306.     for (int i = 0; i < size.y; i++)        // for each line...
  307.     {
  308.     writeChar(0, i, RCELL, wcolor, size.x);    // fill with colored space
  309.     }
  310.  
  311.     for (int lite = FIRST_LIGHT; lite <= LAST_LIGHT; lite++)
  312.     {
  313.     switch (lite)
  314.     {
  315.     case RESPONSE_1:
  316.  
  317.         lightDraw(&textBox->response_1);
  318.         break;
  319.  
  320.     case RESPONSE_2:
  321.  
  322.         lightDraw(&textBox->response_2);
  323.         break;
  324.  
  325.     case READY_LIGHT:
  326.  
  327.         lightDraw(&textBox->ready_light);
  328.         break;
  329.  
  330.     case ANSWER_LIGHT:
  331.  
  332.         lightDraw(&textBox->answer_light);
  333.         break;
  334.  
  335.     case INTERVAL_1:
  336.  
  337.         lightDraw(&textBox->interval_1);
  338.         break;
  339.  
  340.     case INTERVAL_2:
  341.  
  342.         lightDraw(&textBox->interval_2);
  343.         break;
  344.  
  345.     case FAILURE_LIGHT:
  346.  
  347.         // text draw;
  348.         break;
  349.     }
  350.     }
  351. }
  352.  
  353.  
  354. /************************************************************************
  355. ** lightDraw()
  356. **
  357. **    Turns on/off the desired combinations of response lights.
  358. ** Draws the lights according to each light's status.  This status is
  359. ** set by lightOn() or lightOff(), and the lights "wait" for the
  360. ** next order to be re-drawn all at once.
  361. **
  362. *************************************************************************/
  363.  
  364. void
  365. ResponseInterior::lightDraw
  366. (
  367.     ResponseLight *r
  368. )
  369. {
  370.     int width = r->x1 - r->x0 + 1;
  371.     uchar lcolor;
  372.  
  373.     lcolor = r->status ? r->color : textBox->background_color;
  374.  
  375.     for (int i = r->y0; i <= r->y1; i++)    // each line in light
  376.     writeChar                // fill with colored space
  377.     (
  378.         r->x0, i, RCELL, lcolor, width
  379.     );
  380. }
  381.  
  382.  
  383. /************************************************************************
  384. **
  385. **    The following functions do not know about fonts and graphics.
  386. ** They are wholly text-based and Turbo Vision based.
  387. **
  388. *************************************************************************/
  389.  
  390. /************************************************************************
  391. ** light()
  392. **
  393. **    Turns on/off the desired combinations of response lights.
  394. ** Doesn't draw the lights, just sets each status properly, then
  395. ** calls drawView() to draw the lights using draw().
  396. **
  397. *************************************************************************/
  398.  
  399. void
  400. ResponseInterior::light
  401. (
  402.     unsigned char light_mask
  403. )
  404. {
  405.     int lite, temp_mask;
  406.  
  407.     for (lite = FIRST_LIGHT; lite <= LAST_LIGHT; lite++)
  408.     {
  409.     temp_mask = light_mask & 1;        // get right-most bit
  410.  
  411.     if (temp_mask != 0)            // turn a light ON
  412.     {
  413.         switch (lite)
  414.         {
  415.         case RESPONSE_1:
  416.  
  417.             lightOn(&textBox->response_1);
  418.             break;
  419.  
  420.         case RESPONSE_2:
  421.  
  422.             lightOn(&textBox->response_2);
  423.             break;
  424.  
  425.         case READY_LIGHT:
  426.  
  427.             lightOn(&textBox->ready_light);
  428.             break;
  429.  
  430.         case ANSWER_LIGHT:
  431.  
  432.             lightOn(&textBox->answer_light);
  433.             break;
  434.  
  435.         case INTERVAL_1:
  436.  
  437.             lightOn(&textBox->interval_1);
  438.             break;
  439.  
  440.         case INTERVAL_2:
  441.  
  442.             lightOn(&textBox->interval_2);
  443.             break;
  444.  
  445.         case FAILURE_LIGHT:
  446.  
  447.             textBox->failure_light.status = RESP_ON;
  448.             break;
  449.  
  450.         default:
  451.  
  452.             puts("Programmer: you turned on an unused light.\n");
  453.             break;
  454.         }
  455.     }
  456.     else
  457.     {                    // turn a light OFF
  458.         switch (lite)
  459.         {
  460.         case RESPONSE_1:
  461.  
  462.             lightOff(&textBox->response_1);
  463.             break;
  464.  
  465.         case RESPONSE_2:
  466.  
  467.             lightOff(&textBox->response_2);
  468.             break;
  469.  
  470.         case READY_LIGHT:
  471.  
  472.             lightOff(&textBox->ready_light);
  473.             break;
  474.  
  475.         case ANSWER_LIGHT:
  476.  
  477.             lightOff(&textBox->answer_light);
  478.             break;
  479.  
  480.         case INTERVAL_1:
  481.  
  482.             lightOff(&textBox->interval_1);
  483.             break;
  484.  
  485.         case INTERVAL_2:
  486.  
  487.             lightOff(&textBox->interval_2);
  488.             break;
  489.  
  490.         case FAILURE_LIGHT:
  491.  
  492.             textBox->failure_light.status = RESP_OFF;
  493.             break;
  494.         }
  495.     }
  496.     light_mask >>= 1;            // shift to next bit
  497.     }
  498.     drawView();                    // update the view
  499. }
  500.  
  501.  
  502. /************************************************************************
  503. ** lightOn()
  504. ** lightOff()
  505. **
  506. **    Turns on or off a single light.  Since drawing of the lights
  507. ** can be redone at any time, we must always have the current status
  508. ** of lights available.  Hence, these two functions are very simple...
  509. ** they merely set or reset the state flag for the light.
  510. **
  511. *************************************************************************/
  512.  
  513. void
  514. ResponseInterior::lightOn
  515. (
  516.     ResponseLight *r
  517. )
  518. {
  519.     r->status = RESP_ON;
  520. }
  521.  
  522. void
  523. ResponseInterior::lightOff
  524. (
  525.     ResponseLight *r
  526. )
  527. {
  528.     r->status = RESP_OFF;
  529. }
  530.  
  531.  
  532.