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

  1. /************************************************************************
  2. **
  3. ** @(#)face_tv.cpp    01/05/94    Chris Ahlstrom
  4. **
  5. **  ------------------------
  6. **  73340.26@compuserve.com
  7. **  ------------------------
  8. **
  9. **    Let's slowly build a C++ version of the FACE_TV code.  This
  10. ** main program serves as a fairly simple test-bed and demo program
  11. ** for the FACE_TV library.
  12. **
  13. **    To build it, use the makefile provided.
  14. **
  15. ** v. 1.31    Added ResponseDevice code to support interactive
  16. **        between the user and the program in experiments.
  17. **        It includes a "response test" command that just
  18. **        verifies that the button and screen code works
  19. **        properly.
  20. **
  21. ** v. 1.30    Some old code is still in here... I've incorporated
  22. **        code to test a new way of handling configuration
  23. **        files.  It is implemented by creating a
  24. **        SimpleConfiguration object.
  25. **
  26. **        At present, any code in this library that deals
  27. **        with the FilePicker class is much more in danger of
  28. **        of being buggy.
  29. **
  30. **        Also involved is a better way of handling error
  31. **        messages (the class provides a pointer to the
  32. **        error message) which doesn't fit into the
  33. **        errorHandler() scheme, alas.
  34. **
  35. *************************************************************************/
  36.  
  37.  
  38. /************************************************************************
  39. **
  40. ** MAIN_cpp        used for defining variables
  41. ** FACE_TV_cpp        used for definitions peculiar to FACE_TV
  42. ** PROGRAM_VERSION    appears in banners
  43. ** FACE_DOS_PROMPT    prompt that appears on DOS command line
  44. ** FACE_WILDCARD        generic extension for FACE_TV-related files
  45. **
  46. *************************************************************************/
  47.  
  48. #define MAIN_cpp            // only one of these per program
  49. #define FACE_TV_cpp            // only one of these per FACE_TV
  50. #define PROGRAM_VERSION    " 1.32"        // Turbo Vision is 2nd generation
  51.  
  52. #define FACE_DOS_PROMPT    "PROMPT='EXIT' returns to FACE$_$P$G"
  53. #define FACE_WILDCARD    "*.FAC"
  54.  
  55. #include <stdlib.h>        // for exit(), random()
  56. #include <conio.h>        // for video modes
  57. #include <iostream.h>
  58. #include <fstream.h>            // for ifstream
  59. #include <stdio.h>              // for puts() etc
  60. #include <string.h>             // for strlen etc
  61. #include <ctype.h>
  62.  
  63. #define USE_GEN_ERRORS        // include message buffer in announce.h
  64. #define USE_YES_NO_BUTTONS    // include YesNo indirectly in announce.h
  65. #define USE_TURBO_VISION    // use TV version of screenmsg()
  66. #include "announce.h"        // error message and general message code
  67. #include "boxtools.h"        // tools for global annunciator boxes
  68. #include "face_tv.h"        // a good description of the main program
  69. #include "face_tv.men"        // all the menu entries
  70. #include "face_box.men"        // a sample setup for the dialog box
  71.  
  72. #if defined(TESTHEAP)        // { TESTHEAP
  73. #include "heapview.h"        // handy viewer for debugging heap problems
  74. #endif                // } TESTHEAP
  75.  
  76. #include "timerctl.h"        // controlling timers (TimerControl class)
  77. #include "tinpmous.h"        // controlling numbers with the mouse
  78. #include "tv_confg.h"        // SimpleConfiguration class
  79. #include "respwind.h"        // ResponseDevice class
  80.  
  81.  
  82. /************************************************************************
  83. ** Static bar pointers for the main program
  84. *************************************************************************/
  85.  
  86. const NestBar *MenuBarApp::menuBar = &FACEMenuBar;
  87. const StatusBar *MenuBarApp::statusBar = &FACEStatusBar;
  88.  
  89.  
  90. /************************************************************************
  91. ** TFaceApp constructor
  92. *************************************************************************/
  93.  
  94. TFaceApp::TFaceApp()
  95.  :
  96.     TProgInit                    // initialize the desktop
  97.     (
  98.     TFaceApp::initStatusLine,
  99.     TFaceApp::initMenuBar,
  100.     TFaceApp::initDeskTop
  101.     ),
  102.     MenuBarApp    (),                    // initialize menubar
  103.     TBox    (deskTop, dacByteList, midByteList),    // init dialog box
  104.     DosShell    (FACE_DOS_PROMPT),            // set up DOS prompt
  105.     appError    (ERR_NONE),                // start with no errors
  106.  
  107. #ifdef OLD_WAY
  108.     configFile    (new FilePicker(deskTop)),        // main file name
  109. #endif
  110.  
  111.     showBox    ((TDialog *) NULL),            // control box
  112.     equipmentConfig(0)
  113. {
  114.     msgArea = new UserMessages(deskTop, genErrors, genErrorsLength);
  115.  
  116.     /********************************************************************
  117.     ** Should check the following for success (I do in FACE_WIN, but
  118.     ** not here, tsk tsk.)
  119.     *********************************************************************/
  120.  
  121.     equipmentConfig = new SimpleConfiguration
  122.     (
  123.     deskTop, "default.ftv", &equipmentList[0] // default extension "FTV"
  124.     );
  125.  
  126.     /********************************************************************
  127.     ** Let's give a simple test to a useful feature for converting
  128.     ** old code to run with Turbo Vision, by letting the user replace
  129.     ** puts() [or printf(), if the caller formats the string using
  130.     ** sprintf()] calls with calls to screenmsg().
  131.     *********************************************************************/
  132.  
  133.     testsWindow = (TWindowScreen *) 0;
  134.     if (testsWindow == (TWindowScreen *) 0)
  135.     {
  136.     testsWindow = startWindowScreen(deskTop);
  137.     (void) screenmsg
  138.     (
  139.         1, testsWindow, "FACE_TV messages system is running\n"
  140.     );
  141.     (void) screenmsg
  142.     (
  143.         1, testsWindow, "A second message\n\n\n\n\n\n\n\n\n\n"
  144.     );
  145.     (void) screenmsg
  146.     (
  147.         1, testsWindow,
  148.         "A third message to really test this sucker and see...\n\n\n"
  149.         "what it can handle.  Done.\n=============================\n"
  150.     );
  151.     }
  152.  
  153. #if defined(TESTHEAP)                // { TESTHEAP
  154.  
  155.     TRect r = getExtent();            // create the heap view
  156.     r.a.x = r.b.x - 13;
  157.     r.a.y = r.b.y -  1;
  158.     heap = new THeapView(r);
  159.     insert(heap);
  160.  
  161. #endif                        // } TESTHEAP
  162.  
  163.     initTFaceApp();                // set-up initial status
  164. }
  165.  
  166.  
  167. /************************************************************************
  168. ** TFaceApp destructor
  169. **
  170. **    We don't need to delete any objects that have been inserted
  171. ** into the deskTop... the deskTop takes care of calling destroy()
  172. ** for any items still inserted into it (such as testsWindow).
  173. **
  174. *************************************************************************/
  175.  
  176. TFaceApp::~TFaceApp ()
  177. {
  178.  
  179. #if defined(TESTHEAP)                // { TESTHEAP
  180.  
  181.     delete heap;
  182.  
  183. #endif                        // } TESTHEAP
  184.  
  185.     if (equipmentConfig)
  186.     delete equipmentConfig;
  187.     if (msgArea)
  188.     delete msgArea;
  189. }
  190.  
  191.  
  192. /************************************************************************
  193. ** TFaceApp::initTFaceApp ()
  194. **
  195. **    Very simple now, but might read configuration files later.
  196. ** C4350 is another option.  I don't UNDERSTAND how these work.
  197. ** Can't get 50-line mode to come up, and the mouse can move way
  198. ** below the screen if DOS was in 50-line mode before you started
  199. ** the FACE_TV program!!!!
  200. **
  201. **    For now, I like the behavior of matching the DOS screen mode
  202. ** better... if the mode is 50-line, use that, otherwise use the 25-line
  203. ** mode.  So I've commented out the setScreenMode call.
  204. **
  205. **    Maybe need a better way to handle this bloody issue.
  206. **
  207. **    By the way, CONIO.H has more video mode integers than
  208. ** the smxxxx constants in Turbo Vision's SYSTEM.H.  And don't bother
  209. ** checking... CONIO's constants match (in value) those in SYSTEM.
  210. **
  211. *************************************************************************/
  212.  
  213. void
  214. TFaceApp::initTFaceApp ()
  215. {
  216.     textMode    = C80;                // C80 is in conio.h
  217.  
  218.     // textMode    = C80 + C4350;            // C80 is in conio.h
  219.     // setScreenMode(textMode);            // fix the screen
  220. }
  221.  
  222.  
  223. /************************************************************************
  224. ** TFaceApp handleEvent ()
  225. **
  226. **    Note that the old way of reading and writing configuration
  227. ** information is superceded.  Note also that we could add a third
  228. ** configuration command denoted "Save As", copy the calls for the
  229. ** Write command to it, and remove the parameter [FORCE_NEW_NAME]
  230. ** (or change it to PREVIOUS_NAME_UNLESS_INACTIVE, an ugly alternative),
  231. ** to make it more like the standard Save (which normally uses
  232. ** the default name, unless none has been given yet).
  233. **
  234. **    See the TV_CONFG modules for more information.
  235. **
  236. *************************************************************************/
  237.  
  238. void
  239. TFaceApp::handleEvent
  240. (
  241.     TEvent& event
  242. )
  243. {
  244.     ErrorCode he_error = ERR_NONE;
  245.  
  246.     TApplication::handleEvent(event);        // handle the TV stuff
  247.  
  248.     if (event.what == evCommand)
  249.     {
  250.         switch (event.message.command)
  251.     {
  252.     case cmConfigWriteAs:
  253.  
  254.         if (equipmentConfig)
  255.         (void) equipmentConfig->write(FORCE_NEW_NAME);    // get new name
  256.         break;
  257.  
  258.     case cmConfigWrite:
  259.  
  260. #ifdef OLD_WAY            // can't handle error messages
  261.         configFile->fileDialog(FILE_WRITE, FACE_WILDCARD);
  262.         if (configFile->fileAction())
  263.         face_dialogs(FILE_WRITE, configFile->fileSpec());
  264. #else
  265.         if (equipmentConfig)
  266.         (void) equipmentConfig->write();
  267.         break;
  268. #endif
  269.  
  270.     case cmConfigRead:
  271.  
  272. #ifdef OLD_WAY            // can't handle error messages
  273.         configFile->fileDialog(FILE_READ, FACE_WILDCARD);
  274.         if (configFile->fileAction())
  275.         face_dialogs(FILE_READ, configFile->fileSpec());
  276. #else
  277.  
  278.         if (equipmentConfig)
  279.         (void) equipmentConfig->read(FORCE_NEW_NAME);
  280. #endif
  281.         break;
  282.  
  283.     case cmDOS_Cmd:                // DOS shell
  284.  
  285.         shell();                // shell out to DOS
  286.         redraw();                // redraw application screen
  287.         break;
  288.  
  289.     case cmInfo:
  290.  
  291.         msgArea->bannerHandler(FACE_TV_BANNER);
  292.         break;
  293.  
  294.     case cmDacDialog:
  295.  
  296.         (void) doDialog(&dacMenu, &dacDialog);
  297.         break;
  298.  
  299.     case cmMidDialog:
  300.  
  301.         (void) mappedDialog(&midMenu, &midDialog);
  302.         break;
  303.  
  304.     case cmExpDialog:
  305.  
  306.         (void) doDialog(&expMenu, &expDialog);
  307.         break;
  308.  
  309.     case cmDelayDialog:
  310.  
  311.         (void) doDialog(&delayWindow, &delayData);
  312.         break;
  313.  
  314.     case cmResponseTest:
  315.  
  316.         (void) respTest();
  317.         break;
  318.  
  319.     case cmDelayTest:
  320.  
  321.         (void) delayTest(&delayData);
  322.         break;
  323.  
  324.     default:
  325.  
  326.         he_error = ERR_COMMAND_UNIMPLEMENTED;
  327.         break;
  328.     }
  329.         clearEvent(event);            // clear event after handling
  330.     }
  331.     if (he_error != ERR_NONE)            // temp; avoid error message
  332.     {
  333.     msgArea->errorHandler(he_error);
  334.     he_error = ERR_NONE;
  335.     }
  336. }
  337.  
  338.  
  339. /************************************************************************
  340. ** TFaceApp::idle
  341. **
  342. **    This routine provides a nice way to perform any background
  343. ** tasks.  At present, the THeapView task is hardwired into place.
  344. **
  345. *************************************************************************/
  346.  
  347. void
  348. TFaceApp::idle()                // background task-master
  349. {
  350.     TProgram::idle();
  351.  
  352. #if defined(TESTHEAP)                    // { TESTHEAP
  353.  
  354.     heap->update();
  355.  
  356. #endif                            // } TESTHEAP
  357.  
  358. }
  359.  
  360.  
  361. /************************************************************************
  362. ** TFaceApp::face_dialogs()
  363. **
  364. **    Saves and restores all the dialog boxes; they amount to all
  365. ** the configuration we need.
  366. **
  367. **    I should learn to use streams on this stuff; later dude.
  368. **
  369. *************************************************************************/
  370.  
  371. void
  372. TFaceApp::face_dialogs
  373. (
  374.     FileOperation rw,
  375.     char *fname
  376. )
  377. {
  378.     FILE *fptr;
  379.     size_t items;        // number of items successfully written
  380.  
  381.     if (rw == FILE_WRITE)
  382.     {
  383.     if ((fptr = fopen(fname, "wb")) != NULL)
  384.     {
  385.         //items = fwrite(&subDialog, sizeof(subDialog), 1, fptr);
  386.         items=0; //TEMPORARY
  387.         fclose(fptr);
  388.     }
  389.     else
  390.     {
  391.         msgArea->errorHandler(ERR_FILE_OPEN);
  392.     }
  393.     if (items < 1)
  394.         msgArea->errorHandler(ERR_FILE_WRITE);
  395.     }
  396.     else
  397.     {
  398.     if ((fptr = fopen(fname, "rb")) != NULL)
  399.     {
  400.         //items = fread(&subDialog, sizeof(subDialog), 1, fptr);
  401.         fclose(fptr);
  402.     }
  403.     else
  404.     {
  405.         msgArea->errorHandler(ERR_FILE_OPEN);
  406.     }
  407.     if (items < 1)
  408.         msgArea->errorHandler(ERR_FILE_READ);
  409.     }
  410. }
  411.  
  412.  
  413. /************************************************************************
  414. ** TFaceApp::respTest()
  415. **
  416. **    Tests the response device system.
  417. **
  418. **    It is important to note that objects that were inserted into
  419. ** the Turbo Vision deskTop cannot just be deleted.  Instead, they
  420. ** must be destroyed(), as shown below!
  421. **
  422. **    Very very important!!!
  423. **
  424. **    Note that, for the response feedback calls, you can press either
  425. ** 1 or 2, and the feedback lights will be on the same side if the
  426. ** response matched the parameter (either 1 or 2) of the call.  Otherwise,
  427. ** they will be on the opposite side.
  428. **
  429. *************************************************************************/
  430.  
  431. int
  432. TFaceApp::respTest ()
  433. {
  434.     int errcode = 0;
  435.  
  436.     ResponseDevice *r = new ResponseDevice
  437.     (
  438.     KEYBOARD_RESPONSE,
  439.     TRect(2, 2, 70, 21),        // bounds of the window
  440.     "Response Device",        // window's title
  441.     1                // window number
  442.     );
  443.     if (r)                // always check it... saves grief
  444.     {
  445.     deskTop->insert(r);        // bring up the window
  446.  
  447.     msgArea->bannerHandler
  448.     (
  449.         "Press the 1 or 2 key at each pause until the window disappears"
  450.     );
  451.     r->startScreen();
  452.     r->readyLight();
  453.     r->pause();
  454.     r->intervalLight(1);
  455.     r->pause();
  456.     r->intervalLight(2);
  457.     r->pause();
  458.     r->answerLight();
  459.     r->pause();
  460.     r->responseFeedback(1);        // try answering with left...
  461.     r->pause();
  462.     r->responseFeedback(1);        // then right... buttons
  463.     r->pause();
  464.     r->responseFeedback(2);        // try answering with left...
  465.     r->pause();
  466.     r->responseFeedback(2);        // then right... buttons
  467.     r->pause();
  468.  
  469.     deskTop->destroy(r);        // with TV, just can't delete it!!!
  470.     }
  471.     else
  472.     errcode = 1;
  473.  
  474.     return errcode;
  475. }
  476.  
  477.  
  478. /************************************************************************
  479. ** TFaceApp::delayTest()
  480. **
  481. **    Tests the response device system and the delay timer system.
  482. ** Similar to respTest(), but uses delays instead of pauses.
  483. ** The effect is to simulate the screen appearance (just once) of
  484. ** the 2AFC experimental procedure.
  485. **
  486. **    Later, we can add random-interval generation and looping,
  487. ** to be halted by pressing both mouse buttons.
  488. **
  489. **    I really don't like the way timers are handled here.  I must
  490. ** rethink them.
  491. **
  492. *************************************************************************/
  493.  
  494. #define INTERVAL_MAX    2
  495.  
  496. int
  497. TFaceApp::delayTest
  498. (
  499.     DelayParameters *delays
  500. )
  501. {
  502.     int errcode = 0;
  503.  
  504.  
  505.     TimerControl *firsttimer = 0;
  506.     TimerControl *timer = 0;
  507.     TimerControl *previoustimer = 0;
  508.     double dur;
  509.     int goodone = 0;
  510.  
  511.     for (int i = 0; i < DELAY_MAX; i++)
  512.     {
  513.     dur = (double) delays->scaleFactor / 100.0;
  514.     dur *= delays->timerDelay[i];
  515.  
  516.     timer = new TimerControl    // make another timer
  517.     (
  518.         deskTop,            // hook it to TV deskTop
  519.         delays->baseID + i,        // give it an id number
  520.         (unsigned) dur,        // pass the duration
  521.         TIMER_IGNORE_COUNT
  522.     );
  523.     if (timer)
  524.     {
  525.         goodone++;
  526.         if (i == 0)
  527.         firsttimer = timer;
  528.         else
  529.         timer->attach(previoustimer);
  530.         previoustimer = timer;
  531.     }
  532.     else
  533.     {
  534.         break;
  535.     }
  536.     }
  537.     if (goodone < DELAY_MAX)        // some timers not get made?
  538.     {
  539.     deleteTimer(firsttimer);
  540.     msgArea->bannerHandler("Timer creation error(s)");
  541.     return errcode = 2;
  542.     }
  543.  
  544.     TimerControl *before;             // DELAY_BEFORE_INTERVAL
  545.     TimerControl *stim;                 // DELAY_FOR_STIMULUS
  546.     TimerControl *between;             // DELAY_BETWEEN_INTERVAL
  547.     TimerControl *feedback;            // DELAY_FOR_FEEDBACK
  548.  
  549.     before    = firsttimer;
  550.     stim    = before->next();
  551.     between    = stim->next();
  552.     feedback    = between->next();
  553.  
  554.     ResponseDevice *r = new ResponseDevice
  555.     (
  556.     expDialog.responseDevice,    // instead of KEYBOARD_RESPONSE,
  557.     TRect(2, 2, 70, 21),        // bounds of the window
  558.     "Response Device",        // window's title
  559.     1                // window number
  560.     );
  561.     if (r)                // always check it... saves grief
  562.     {
  563.     deskTop->insert(r);        // bring up the window
  564.  
  565.     msgArea->bannerHandler
  566.     (
  567.         "Press the 1 or 2 key after the 4 lights blink"
  568.     );
  569.  
  570.     /****************************************************************
  571.     ** As you can see, we use a linked list of timers.  Don't get
  572.     ** yourself thinking that we have an array of timers.
  573.     *****************************************************************/
  574.  
  575.     int mkey;                // the response made by user
  576.  
  577.     r->startScreen();
  578.  
  579.     r->readyLight();
  580.     errcode = before->timerDelay();
  581.  
  582.     for (int interval = 1; interval <= INTERVAL_MAX; interval++)
  583.     {
  584.         r->intervalLight(interval);
  585.         errcode = stim->timerDelay();
  586.         if (interval < INTERVAL_MAX)
  587.         errcode = between->timerDelay();
  588.     }
  589.     r->answerLight();
  590.     mkey = r->responseFeedback(1);
  591.  
  592.     if (mkey != BOTH_BUTTONS)
  593.         errcode = feedback->timerDelay();
  594.  
  595.     deskTop->destroy(r);        // with TV, just can't delete it!!!
  596.  
  597.     if (mkey == BOTH_BUTTONS)
  598.         msgArea->bannerHandler("You aborted!  No problem...");
  599.     }
  600.     else
  601.     errcode = 1;
  602.  
  603.     deleteTimer(firsttimer);
  604.  
  605.     return errcode;
  606. }
  607.  
  608.  
  609. /************************************************************************
  610. ** Sample control-function for FACE_TV
  611. **
  612. **    All this function does is display the value on the desktop
  613. ** in a little dialog box.  As the mouse moves, both the original
  614. ** field and the value should scroll together
  615. *************************************************************************/
  616.  
  617. static TDialog *showBox = (TDialog *) NULL;
  618. static TDeskTop *faceDeskTop;        // indirect access to deskTop
  619.  
  620. int
  621. showFloatInBox
  622. (
  623.     double value
  624. )
  625. {
  626.     if (showBox)
  627.     {
  628.     closeBox(showBox, 0);
  629.     }
  630.     showBox = floatBox
  631.     (
  632.     faceDeskTop, 12, 10, "Ctrl", value
  633.     );
  634.     return 0;                // bogus error code for now
  635. }
  636.  
  637.  
  638. /************************************************************************
  639. ** FACE_TV main routine
  640. **
  641. **    Normally, we'd just declare "TFaceApp myApp;" and do a
  642. ** "myApp.run()", but we need to test if it was created, for error-
  643. ** checking.
  644. **
  645. *************************************************************************/
  646.  
  647.  
  648. int
  649. main()
  650. {
  651.     TFaceApp *myApp = new TFaceApp();
  652.  
  653.     if (myApp)
  654.     {
  655.     faceDeskTop = myApp->tBoxTop;    // need global access to desktop
  656.     myApp->run();            // run the main program loop
  657.     if (showBox)            // did we leave a showBox open?
  658.         closeBox(showBox, 0);    // yes, get rid of it
  659.     delete myApp;            // get rid of everything
  660.     return 0;            // everything worked
  661.     }
  662.     else
  663.     {
  664.     return 1;            // could not run the program
  665.     }
  666. }
  667.  
  668.