home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 301_01 / dcuwcu.c < prev    next >
C/C++ Source or Header  |  1989-12-30  |  21KB  |  942 lines

  1. /*
  2.  * the Hollywood principle: don't call us, we'll call you
  3.  * 
  4.  * This package uses pd mouse routines and Turbo C graphics
  5.  * to implement a mouse, menu, and form handler.  An application
  6.  * program provides the following routines:
  7.  *    start(ac, av, ep)    - initialization
  8.  *    menu(m, i)        - menu m, item i was selected
  9.  *    button(b, x, y)        - unbound button was pressed 
  10.  *      keyboard(key)        - keyboard character struck
  11.  *      timer(t)                - a timer has gone off
  12.  *
  13.  * This package also provides the following interface routines:
  14.  *    finish()        - all done
  15.  *    add_menu(m, mdef)    - add a menu to the system
  16.  *      menu_state(m, on)       - turn a menu on or off
  17.  *    menu_item(m, i, str)    - change a menu item's string
  18.  *    mouse_state(on)        - turn the mouse on or off
  19.  *      mouse_shape(type, ...)    - set the mouse bitmap
  20.  *      add_timer(t, wait)    - add a timer to the system
  21.  *    form(fdef, argptr,...)    - have user fill out a form
  22.  * 
  23.  * Form definition is pretty complicated.  It's somewhat like scanf in
  24.  * that it uses a format string and %Nd or %Ns to define an input
  25.  * field.  A %[str] defines an exit button and a %(str) defines a regular
  26.  * button.  If you select an exit button, you leave the form.  A
  27.  * regular button has will toggle a value.  A '|' in the definition
  28.  * string starts a new line.
  29.  *
  30.  * If no Microsoft compatible mouse driver is installed, it works
  31.  * with keyboard input with the following conventions:
  32.  *    cursor keys control the mouse
  33.  *    F1 simulates a button 1 key depression (and release)
  34.  *    F2 will enter a menu, a second F2 leaves it
  35.  *
  36.  * Copyright Mark A. Johnson, 1989
  37.  */
  38.  
  39. #include <stdio.h>
  40. #include <graphics.h>
  41. #include <bios.h>
  42.  
  43. #define SAVE        5000    /* constant sizes            */
  44. #define MAXITEM        30
  45. #define MAXMENU        10
  46. #define MAXFORM        20
  47. #define MAXTIMER    10
  48.  
  49. #define TXT_WD        8    /* text height and width        */
  50. #define TXT_HT        8
  51.  
  52. #define TITLE    't'        /* form item types            */
  53. #define NUMBER    'd'
  54. #define STRING    's'
  55. #define EXIT    'e'
  56. #define BUTTON    'b'
  57. #define RADIO    'r'
  58.  
  59. #define ESC    27        /* constants for keyboard handling    */
  60. #define ENTER    '\r'
  61. #define TAB    '\t'
  62. #define F1    256
  63. #define F2    257
  64. #define UP    258
  65. #define DOWN    259
  66. #define LEFT    260
  67. #define RIGHT    261
  68.  
  69. #define INS_CHR    17        /* editing cursor for forms        */
  70.  
  71. #define MS_PER_TICK    64    /* milliseconds per tick (from bios)    */
  72.  
  73. typedef struct { int wid, ht; char buf[10]; } Mbits;
  74. typedef struct { char type, row, col, radio, size, *str, *data; } Form;
  75. typedef struct { char **item, size, handle, on; } Menu;
  76. typedef struct { char handle; long wait; } Timer;
  77.  
  78. static char mouse_ptr[] = { 
  79.     0xFC, 0xFC, 0xF0, 0xF8, 0xDC, 0xCE, 0x07, 0x02 
  80. };
  81. static char mouse_cross[] = {
  82.     0x18, 0x18, 0x18, 0xFF, 0xFF, 0x18, 0x18, 0x18
  83. };
  84. static Mbits mbits = {
  85.     7, 7, { 0xFC, 0xFC, 0xF0, 0xF8, 0xDC, 0xCE, 0x07, 0x02, 0, 0 }
  86. };
  87. int Mx, My;            /* current mouse coordinates        */
  88. static int Mhotx=0, Mhoty=0;    /* offset for mouse cursor hotspot    */
  89. static int Mshow;        /* is mouse showing or not        */
  90. static int Mpresent;        /* is a mouse present or not        */
  91.  
  92. int Maxx, Maxy;            /* graphics driver position range    */
  93. int MaxColor;            /* graphics driver color range        */
  94.  
  95. int MaxSave;            /* keep track of max size save needed    */
  96. static char save[SAVE];        /* buffer to save patches of screen    */
  97.  
  98. static char *items[MAXITEM];    /* menu item table            */
  99. static int last_item;        /* size of menu table            */
  100. static Menu menus[MAXMENU];    /* menu descriptor table        */
  101. static int last_menu;        /* size of menu descriptor table    */
  102.  
  103. static Form forms[MAXFORM];    /* form table                */
  104. static int fcnt;        /* number of items in the form table    */
  105. static char fbuf[100];        /* buffer for the form def'tion string    */
  106. static int fwid;        /* max width of the form        */
  107. static int fht;            /* max height of the form        */
  108. static int fl, ft, fr, fb;    /* form box coordinates            */
  109. static char ebuf[100];        /* edit buffer for form item        */
  110. static int ebufx;        /* index into the edit buffer        */
  111. static int edit;        /* index of current editable item    */
  112.  
  113. static Timer timers[MAXTIMER];    /* a set of timers            */
  114. static int last_timer;        /* number of active timers        */
  115. static long last_tick;        /* last tick of the old ticker        */
  116.  
  117. main(argc, argv, envp) char **argv, **envp; {
  118.     int b, x, y;
  119.     int driver, mode;
  120.  
  121.     Mx = My = 0;
  122.     if ((Mpresent = ms_init()) == 0)
  123.         printf("no mouse!\n");
  124.  
  125.     driver = mode = DETECT; /* detect */
  126.     initgraph(&driver, &mode, ".");
  127.     if (driver < 0) {
  128.         printf("cannot open graph driver, driver=%d\n", driver);
  129.         exit(1);
  130.     }
  131.     start(argc, argv, envp);
  132.     Maxx = getmaxx();
  133.     Maxy = getmaxy();
  134.     MaxColor = getmaxcolor();
  135.     last_tick = biostime(0, 0L);
  136.     if (Mpresent)
  137.         mouse_appl();
  138.     else    key_appl();
  139. }
  140.  
  141. /* drive the application from the mouse */
  142.  
  143. static
  144. mouse_appl() {
  145.     int b, x, y;
  146.     ms_setspeed(10, 10);
  147.     ms_hbounds(0, Maxx);
  148.     ms_vbounds(0, Maxy);
  149.     mouse_state(1);
  150.     while (1) {
  151.         do_timer();
  152.         if (kbhit()) {
  153.             mouse_state(0);
  154.             keyboard(getch());
  155.             mouse_state(1);
  156.         }
  157.         if (ms_button()) {
  158.             delay(50);
  159.             if ((b = ms_button()) != 0) {
  160.                 mouse_state(0);
  161.                 if (b == 2)
  162.                     do_menu();
  163.                 else    {
  164.                     button(b, Mx, My);
  165.                     while (ms_button())
  166.                         ;
  167.                 }
  168.                 mouse_state(1);
  169.             }
  170.         }
  171.         ms_motion(&x, &y);
  172.         if (x || y) move_mouse(x, y);
  173.     }
  174. }
  175.  
  176. /* drive the application from the keyboard */
  177.  
  178. static
  179. key_appl() {
  180.     int c, x, y;
  181.     mouse_state(1);
  182.     while (1) {
  183.         do_timer();
  184.         if (kbhit()) {
  185.             c = key();
  186.             if (c == F1) {
  187.                 mouse_state(0);
  188.                 button(1, Mx, My);
  189.                 mouse_state(1);
  190.             }
  191.             else if (c == F2) {
  192.                 mouse_state(0);
  193.                 do_menu();
  194.                 mouse_state(1);
  195.             }
  196.             else if (is_arrow(c, &x, &y))
  197.                 move_mouse(x, y);
  198.             else    {
  199.                 mouse_state(0);
  200.                 keyboard(c);
  201.                 mouse_state(1);
  202.             }
  203.         }
  204.     }
  205. }
  206.  
  207. /* change the mouse bitmap */
  208.  
  209. mouse_shape(type, data, hx, hy) char *data; {
  210.     int i;
  211.     char *p;
  212.     int m_on = Mshow;
  213.     if (m_on) mouse_state(0);
  214.     switch (type) {
  215.     case 0: 
  216.         p = mouse_ptr; 
  217.         Mhotx = Mhoty = 0;
  218.         break;
  219.     case 1: 
  220.         p = mouse_cross; 
  221.         Mhotx = Mhoty = 3;
  222.         break;
  223.     case 2: 
  224.         p = data; 
  225.         Mhotx = hx;
  226.         Mhoty = hy;
  227.         break;
  228.     }
  229.     for (i = 0; i < 8; i++)
  230.         mbits.buf[i] = p[i];
  231.     if (m_on) mouse_state(1);
  232. }
  233.  
  234. /* handle keyboard input, map F1, etc to single keys */
  235.  
  236. static
  237. key() {
  238.     int c;
  239.     static int back = 0;
  240.     if (back) {
  241.         c = back;
  242.         back = 0;
  243.     }
  244.     else    {
  245.         c = getch();
  246.         if (c == 0) {
  247.             switch (c = getch()) {
  248.             case 59: c = F1; break;
  249.             case 60: c = F2; break;
  250.             case 72: c = UP; break;
  251.             case 80: c = DOWN; break;
  252.             case 77: c = RIGHT; break;
  253.             case 75: c = LEFT; break;
  254.             default: back = c; c = 0; break;
  255.             }
  256.         }
  257.     }
  258.     return c;
  259. }
  260.  
  261. /* is the given key an arrow key, if so, set the delta x,y pointers */
  262.  
  263. static
  264. is_arrow(c, xp, yp) int c, *xp, *yp; {
  265.     static int lastc, cnt;
  266.     if (c != lastc) {
  267.         lastc = c;
  268.         cnt = 1;
  269.     }
  270.     else    cnt++;
  271.     *xp = *yp = 0;
  272.     switch (c) {
  273.     case UP:
  274.         *yp = -cnt;
  275.         break;
  276.     case DOWN:
  277.         *yp = cnt;
  278.         break;
  279.     case LEFT:
  280.         *xp = -cnt;
  281.         break;
  282.     case RIGHT:
  283.         *xp = cnt;
  284.         break;
  285.     default:
  286.         return 0;
  287.     }
  288.     return 1;
  289. }
  290.  
  291. /* the application requests termination */
  292.  
  293. finish() {
  294.     closegraph();
  295.     exit(0);
  296. }
  297.  
  298. /* move the mouse a bit */
  299.  
  300. static
  301. move_mouse(dx, dy) {
  302.     mouse_state(0);
  303.     Mx += dx;
  304.     if (Mx < 0) Mx = 0;
  305.     if (Mx > Maxx) Mx = Maxx;
  306.     My += dy;
  307.     if (My < 0) My = 0;
  308.     if (My > Maxy) My = Maxy;
  309.     mouse_state(1);
  310. }
  311.  
  312. /* turn the mouse picture on or off */
  313.  
  314. mouse_state(on) {
  315.     if (on ^ Mshow)
  316.         putimage(Mx-Mhotx, My-Mhoty, &mbits, XOR_PUT);
  317.     Mshow = on;
  318. }
  319.  
  320. /* add a timer to the system, wait is in milliseconds */
  321.  
  322. add_timer(handle, wait) long wait; {
  323.     int i, j;
  324.     long total = 0L;
  325.  
  326.     /* find the insertion point */
  327.     for (i = 0; i < last_timer; i++) {
  328.         if (wait < total + timers[i].wait)
  329.             break;
  330.         total += timers[i].wait;
  331.     }
  332.  
  333.     /* shuffle things down if necessary */
  334.     if (i < last_timer) { 
  335.         for (j = last_timer; j > i; j--)
  336.             timers[j] = timers[j-1];
  337.     }
  338.     
  339.     /* insert the new timer, adjust wait to incr over previous */
  340.     wait -= total;
  341.     timers[i].handle = handle;
  342.     timers[i].wait = wait;
  343.  
  344.     /* fix up the timers that follow it */
  345.     for ( ; i < last_timer; i++) 
  346.         timers[i].wait -= wait;
  347.     last_timer++;
  348.  
  349. #ifdef DEBUG
  350.     /* debugging */
  351.     for (i = 0; i < last_timer; i++)
  352.         message(300, i * 10, "%d: h=%d w=%ld",
  353.             i, timers[i].handle, timers[i].wait);
  354. #endif
  355. }
  356.  
  357. /* parse a menu definition string and add it to menu table */
  358.  
  359. add_menu(handle, menu_def) char *menu_def; {
  360.     int start = last_item;
  361.     char *s, *strtok();
  362.     if (last_menu >= MAXMENU || last_item >= MAXITEM) {
  363.         trouble("too many menu's");
  364.         return 0;
  365.     }
  366.     s = strtok(menu_def, ":");
  367.     menus[last_menu].item = &items[last_item];
  368.     menus[last_menu].handle = handle;
  369.     menus[last_menu].on = 1;
  370.     while (s && last_item < MAXITEM) {
  371.         items[last_item++] = s;
  372.         s = strtok(NULL, "|");
  373.     }
  374.     menus[last_menu].size = last_item - start;
  375.     last_menu++;
  376. }
  377.  
  378. /* find a menu index, given a handle */
  379.  
  380. static
  381. find_menu(handle) {
  382.     int i;
  383.     for (i = 0; i < last_menu; i++)
  384.         if (menus[i].handle == handle)
  385.             return i;
  386.     return -1;
  387. }
  388.  
  389. /* activate or deactivate a menu */
  390.  
  391. menu_state(handle, new_state) {
  392.     Menu m;
  393.     int i = find_menu(handle);
  394.     if (i >= 0) {
  395.         if (new_state == 1 && !menus[i].on) {
  396.             menus[i].on = 1;
  397.             menu_top(i);
  398.             return 1;
  399.         }
  400.         else if (new_state == 0 && menus[i].on) {
  401.             menus[i].on = 0;
  402.             m = menus[i];
  403.             while (i < last_menu-1 && menus[i+1].on) {
  404.                 menus[i] = menus[i+1];
  405.                 i++;
  406.             }
  407.             menus[i] = m;
  408.             return 0;
  409.         }
  410.         else    return menus[i].on;
  411.     }
  412.     else    return -1;
  413. }
  414.  
  415. /* move a menu item to the top spot */
  416.  
  417. static
  418. menu_top(i) {
  419.     Menu m;
  420.     m = menus[0];
  421.     menus[0] = menus[i];
  422.     while (i > 0 && !menus[i-1].on) {
  423.         menus[i] = menus[i-1];
  424.         i--;
  425.     }
  426.     menus[i] = m;
  427. }
  428.  
  429. /* change a menu item */
  430.  
  431. menu_item(handle, index, item) char *item; {
  432.     int i = find_menu(handle), j;
  433.     char **p;
  434.     if (i >= 0 && index > 0 && index < menus[i].size) {
  435.         p = menus[i].item;
  436.         p[index] = item;
  437.         return 1;
  438.     }
  439.     return 0;
  440. }
  441.  
  442. /* handle the application timers */
  443.  
  444. static
  445. do_timer() {
  446.     int i, h;
  447.     long ticks = (biostime(0, 0L) - last_tick) * MS_PER_TICK;
  448.     Timer *t = &timers[0];
  449.  
  450.     if (last_timer > 0) {
  451.         t->wait -= ticks;
  452.         while (last_timer > 0 && t->wait <= 0) {
  453.             h = t->handle;
  454.             last_timer--;
  455.             for (i = 0; i < last_timer; i++)
  456.                 timers[i] = timers[i+1];
  457.             timer(h);
  458.         }
  459.     }
  460.     last_tick = biostime(0, 0L);
  461. }
  462.  
  463. /* handle a menu selection */
  464.  
  465. static
  466. do_menu() {
  467.     int i, x, y, c, lasti, w, h;
  468.     int mcnt = 0, mwid = 0, handle = menus[0].handle;
  469.     int ml, mt, mr, mb;     /* basic menu coordinates */
  470.     int tl, tt, tr, tb;     /* total menu coordinates */
  471.     int active;        /* active menu's */
  472.     char **item = menus[0].item; /* pointer to menu items */
  473.  
  474.     /* figure out how many menu's are active */
  475.     for (i = 0; i < last_menu && menus[i].on; i++)
  476.         ;
  477.     active = i;
  478.             
  479.     /* figure out size of the base menu */
  480.     mcnt = menus[0].size;
  481.     for (i = 0; i < mcnt; i++) {
  482.         c = strlen(item[i]) + 1;
  483.         if (c > mwid) mwid = c;
  484.     }
  485.     for (i = 1; i < active; i++) {
  486.         c = strlen(*menus[i].item) + 1;
  487.         if (c > mwid) mwid = c;
  488.     }
  489.  
  490.     /* compute base menu screen size */
  491.     ml = Mx;
  492.     mt = My;
  493.     mr = ml + (mwid * TXT_WD);
  494.     mb = mt + (mcnt * (TXT_HT+1)) + 2;
  495.  
  496.     /* compute total menu patch size and save it */
  497.     tl = ml;
  498.     tt = mt - ((active - 1) * (TXT_HT+1));
  499.     tr = mr + (active * (TXT_WD/2));
  500.     tb = mb;
  501.     if (tl < 0 || tr > Maxx || tt < 0 || tb > Maxy)
  502.         return;
  503.     if ((i = imagesize(tl, tt, tr, tb)) > SAVE) {
  504.         trouble("SAVE needs to be > %d", i);
  505.         return;
  506.     }
  507.     else if (i >= MaxSave) MaxSave = i;
  508.  
  509.     getimage(tl, tt, tr, tb, save);
  510.     setfillstyle(EMPTY_FILL, 0);
  511.     bar(tl, tt, tr, tb);
  512.     
  513.     /* display menu items */
  514.     setcolor(MaxColor);
  515.     setfillstyle(CLOSE_DOT_FILL, MaxColor);
  516.     bar(ml, mt, mr, mt+TXT_HT);
  517.     rectangle(ml, mt, mr, mb);
  518.     line(ml, mt+9, mr, mt+(TXT_HT+1));
  519.     y = mt + 2;
  520.     x = ml + (TXT_WD/2);
  521.     for (i = 0; i < mcnt; i++, y += (TXT_HT+1))
  522.         outtextxy(x, y, item[i]);
  523.  
  524.     /* display other menus (headers only) */
  525.     y = mt - (TXT_HT+1);
  526.     x = ml + (TXT_WD/2);
  527.     w = mr - ml;
  528.     h = mb - mt;
  529.     for (i = 1; i < active; i++, x += (TXT_WD/2), y -= (TXT_HT+1)) {
  530.         bar(x, y, x+w, y+TXT_HT);
  531.         outtextxy(x+(TXT_WD/2), y+1, *menus[i].item);
  532.         moveto(x, y+(TXT_HT+1));
  533.         linerel(0, -(TXT_HT+1));
  534.         linerel(w, 0);
  535.         linerel(0, h);
  536.         linerel(-(TXT_WD/2), 0);
  537.     }
  538.  
  539.     /* now track the mouse and look for a release */
  540.     lasti = i = -1;
  541.     mouse_state(1);
  542.     while (1) {
  543.         if (Mpresent) {
  544.             if (ms_button() != 2)
  545.                 break;
  546.             ms_motion(&x, &y);
  547.         }
  548.         else    {
  549.             c = key();
  550.             if (c == F2)
  551.                 break;
  552.             is_arrow(c, &x, &y);
  553.         }
  554.         if (x || y) {
  555.             move_mouse(x, y);
  556.             i = mouse_in_item(Mx, My, ml, mt+2, mr, mb);
  557.             if (i != lasti) {
  558.                 menu_invert(lasti, mcnt, ml, mr, mt);
  559.                 menu_invert(i, mcnt, ml, mr, mt);
  560.                 lasti = i;
  561.             }
  562.         }
  563.     }
  564.  
  565.     /* check to see if other menu was chosen */
  566.     y = mouse_in_item(Mx, My, tl, tt, tr, mt);
  567.     if (y >= 0) {
  568.         y = active - 1 - y;
  569.         menu_top(y);
  570.     }
  571.  
  572.     /* put things back the way they were */
  573.     mouse_state(0);
  574.     putimage(tl, tt, save, COPY_PUT);
  575.  
  576.     /* call the application menu routine if selected */
  577.     if (i >= 1 && i < mcnt) menu(handle, i);
  578. }
  579.  
  580. /* highlight (or de-highlight) a menu item by inverting it */
  581.  
  582. static
  583. menu_invert(i, mcnt, l, r, t) {
  584.     if (i >= 1 && i < mcnt) {
  585.         t = t + (i * (TXT_HT+1)) + 2;
  586.         invert(l+2, t-1, r-2, t+(TXT_HT-1));
  587.     }
  588. }
  589.  
  590. /* see if the given mouse coordinates are on top of a menu item */
  591.  
  592. static
  593. mouse_in_item(x, y, l, t, r, b) {
  594.     if (x >= l && x <= r && y >= t && y <= b) {
  595.         return (y - t) / (TXT_HT+1);
  596.     }
  597.     else    return -1;
  598. }
  599.  
  600. /* invert a (small) patch of the screen */
  601.  
  602. static
  603. invert(l, t, r, b) {
  604.     static char buf[100];
  605.     getimage(l, t, r, b, buf);
  606.     putimage(l, t, buf, NOT_PUT);
  607. }
  608.  
  609. /* parse a form definition string */
  610.  
  611. static
  612. parse_form(frm, argp) char *frm, **argp; {
  613.     Form *fp;
  614.     char *s = fbuf;
  615.     int i, c = 0, n, b;
  616.     fcnt = fht = fwid = 0;
  617.     strcpy(fbuf, frm);
  618.     while (*s && fcnt < MAXFORM) {
  619.         if (*s == ' ') { /* skip spaces */
  620.             *s++ = 0;
  621.             c++;
  622.         }
  623.         else if (*s == '|') { /* start a new line */
  624.             *s++ = 0;
  625.             if (c > fwid) fwid = c;
  626.             fht++;
  627.             c = 0;
  628.         }            
  629.         else if (*s == '%') { /* data field */
  630.             *s++ = 0;
  631.             fp = &forms[fcnt++];
  632.             fp->row = fht;
  633.             fp->col = c;
  634.             fp->size = 0;
  635.             fp->data = *argp++;
  636.             if ((b = *s) == '[' || b == '{') { /* buttons */
  637.                 fp->type = (b == '{' ? EXIT : BUTTON);
  638.                 fp->str = s;
  639.                 b = (b == '[' ? ']' : '}');
  640.                 while (*s && *s++ != b) {
  641.                     if (*s == ':')
  642.                         fp->type = RADIO;
  643.                     c++;
  644.                 }
  645.                 if (fp->type == RADIO)
  646.                     radio_set(fp);
  647.                 c += 2;
  648.             }
  649.             else    { /* number or a string */
  650.                 n = 0;
  651.                 while (*s && *s >= '0' && *s <= '9')
  652.                     n = n * 10 + (*s++ - '0');
  653.                 fp->size = n;
  654.                 c += (n + 2);
  655.                 if (*s == 'd' || *s == 's')
  656.                     fp->type = *s++;
  657.                 else    trouble("bad form: '%s'\n", frm);
  658.             }
  659.         }
  660.         else    { /* title field */
  661.             fp = &forms[fcnt++];
  662.             fp->row = fht;
  663.             fp->col = c;
  664.             fp->type = TITLE;
  665.             fp->str = s;
  666.             fp->size = 0;
  667.             while (*s && *s != '|' && *s != '%') {
  668.                 c++;
  669.                 s++;
  670.             }
  671.         }
  672.     }
  673.     if (c > fwid) fwid = c;
  674.     fht++;
  675.     return 1;
  676. }
  677.  
  678. /* do a form for the user */
  679.  
  680. form(frm, args) char *frm; {
  681.     int c, i, x, y;
  682.     char *s;
  683.  
  684.     /* create the form */
  685.     if (!parse_form(frm, &args)) {
  686.         trouble("bad form: '%s'\n", frm);
  687.         return 0;
  688.     }
  689.  
  690.     /* convert text counts to pixel sizes */
  691.     fht = fht * (TXT_HT+1) + 2;
  692.     fwid = fwid * TXT_WD + 6;
  693.  
  694.     /* center the form, save area underneath, and clear it out */
  695.     fl = Maxx/2 - fwid/2;
  696.     ft = Maxy/2 - fht/2;
  697.     fb = ft + fht;
  698.     fr = fl + fwid;
  699.     if ((i = imagesize(fl, ft, fr, fb)) > SAVE) {
  700.         trouble("SAVE needs to be > %d", i);
  701.         return 0;
  702.     }
  703.     else if (i > MaxSave) MaxSave = i;
  704.     getimage(fl, ft, fr, fb, save);
  705.     setfillstyle(EMPTY_FILL, 0);
  706.     bar(fl, ft, fr, fb);
  707.     setcolor(MaxColor);
  708.     rectangle(fl, ft, fr, fb);
  709.  
  710.     /* draw the form */
  711.     edit = -1;
  712.     for (i = 0; i < fcnt; i++) {
  713.         item_get(ebuf, &forms[i]);
  714.         switch (forms[i].type) {
  715.         case NUMBER: case STRING:
  716.             if (edit == -1) edit = i;
  717.             break;
  718.         }
  719.         item_show(ebuf, &forms[i], edit == i);
  720.     }
  721.     if (edit != -1) {
  722.         item_get(ebuf, &forms[edit]);
  723.         ebufx = strlen(ebuf);
  724.     }
  725.  
  726.     /* get the data */
  727.     mouse_state(1);
  728.     while (1) {
  729.         if (Mpresent) {
  730.             if (ms_button() == 1 && form_button())
  731.                 break;
  732.             ms_motion(&x, &y);
  733.             if (x || y) move_mouse(x, y);
  734.             if (kbhit() && (c = form_key(getch())) != 0)
  735.                 break;
  736.         }
  737.         else    {
  738.             c = key();
  739.             if (is_arrow(c, &x, &y))
  740.                 move_mouse(x, y);
  741.             else if (c == F1 && form_button())
  742.                 break;
  743.             else if ((c = form_key(c)) != 0)
  744.                 break;
  745.         }
  746.     }
  747.  
  748.     /* finish up */
  749.     mouse_state(0);
  750.     putimage(fl, ft, save, COPY_PUT);
  751.     return c == ESC ? 0 : 1;
  752. }
  753.  
  754. /* handle a mouse button 1 push during form processing */
  755.  
  756. static
  757. form_button() {
  758.     int i, l, t, r, b;
  759.     Form *fp;
  760.     for (i = 0; i < fcnt; i++) {
  761.         fp = &forms[i];
  762.         switch (fp->type) {
  763.         case BUTTON: case EXIT: case RADIO:
  764.             l = fl + fp->col * TXT_WD;
  765.             t = ft + fp->row * (TXT_HT+1);
  766.             r = l + strlen(fp->str) * TXT_WD;
  767.             b = t + (TXT_HT+1);
  768.             if (Mx >= l && Mx <= r && My >= t && My <= b) {
  769.                 if (fp->type == RADIO)
  770.                     radio_button(fp, (Mx - l)/TXT_WD);
  771.                 else    *fp->data = !*fp->data;
  772.                 mouse_state(0);
  773.                 item_show(fp->str, fp, 0);
  774.                 mouse_state(1);
  775.                 return (fp->type == EXIT);
  776.             }
  777.             break;
  778.         }
  779.     }
  780.     return 0;
  781. }
  782.  
  783. /* handle selection of a radio button item */
  784.  
  785. static
  786. radio_button(fp, cx) Form *fp; {
  787.     int i, n;
  788.     char *s = fp->str;
  789.     for (i = n = 0; s[i] && i < cx; i++) {
  790.         if (s[i] == ':')
  791.             n++;
  792.     }
  793.     *fp->data = n;
  794.     radio_set(fp);
  795. }
  796.  
  797. /* set up the .radio and .size fields for a radio button */
  798.  
  799. static
  800. radio_set(fp) Form *fp; {
  801.     char *s = fp->str;
  802.     int i, n = *fp->data;
  803.     for (i = 1; s[i] && n; ) {
  804.         if (s[i++] == ':') 
  805.             n--;
  806.     }
  807.     if (s[i] == 0) { /* bogus data value */
  808.         fp->radio = fp->size = 0;
  809.     }
  810.     else    {
  811.         fp->radio = i;
  812.         while (s[i] && s[i] != ':' && s[i] != ']')
  813.             i++;
  814.         fp->size = i;
  815.     }
  816. }
  817.  
  818. /* handle a keyboard character pressed during form processing */
  819.  
  820. static
  821. form_key(c) {
  822.     int r = 0;
  823.     mouse_state(0);
  824.     if (c == ESC || c == ENTER) {
  825.         if (edit != -1) item_update(ebuf, &forms[edit]);
  826.         r = c;
  827.     }
  828.     else if (c == TAB) {
  829.         if (edit != -1) {
  830.             item_update(ebuf, &forms[edit]);
  831.             item_show(ebuf, &forms[edit], 0);
  832.             while (1) {
  833.                 if (++edit >= fcnt) edit = 0;
  834.                 if (forms[edit].type == STRING ||
  835.                     forms[edit].type == NUMBER)
  836.                     break;
  837.             }
  838.             item_get(ebuf, &forms[edit]);
  839.             ebufx = strlen(ebuf);
  840.             item_show(ebuf, &forms[edit], 1);
  841.         }
  842.     }
  843.     else if (edit != -1) {
  844.         if (c == '\b' && ebufx > 0)
  845.             ebuf[--ebufx] = 0;
  846.         else if (c >= ' ') {
  847.             ebuf[ebufx++] = c;
  848.             ebuf[ebufx] = 0;
  849.         }
  850.         item_show(ebuf, &forms[edit], 1);
  851.     }
  852.     mouse_state(1);
  853.     return r;
  854. }
  855.  
  856. /* get the string of the form item value into the buffer */
  857.  
  858. static
  859. item_get(buf, fp) char *buf; Form *fp; {
  860.     switch (fp->type) {
  861.     case TITLE: case BUTTON: case EXIT: case RADIO:
  862.         strcpy(buf, fp->str);
  863.         break;
  864.     case STRING:
  865.         strcpy(buf, fp->data);
  866.         break;
  867.     case NUMBER:
  868.         sprintf(buf, "%d", *(int *)fp->data);
  869.         break;
  870.     }
  871. }
  872.  
  873. /* show the given item, string rep in buf */
  874.  
  875. static
  876. item_show(buf, fp, edit) char *buf; Form *fp; {
  877.     int l, t, r, b, n;
  878.     static char sbuf[100];
  879.     strcpy(sbuf, buf);
  880.     n = strlen(sbuf);
  881.     if (edit) sbuf[n++] = INS_CHR;
  882.     if (fp->type == NUMBER || fp->type == STRING) {
  883.         while (n < fp->size) sbuf[n++] = '_';
  884.     }
  885.     sbuf[n] = 0;
  886.     l = fl + 3 + fp->col * TXT_WD;
  887.     t = ft + 2 + fp->row * (TXT_HT+1);
  888.     r = l + n * TXT_WD;
  889.     b = t + TXT_HT;
  890.     if (fp->type == NUMBER || fp->type == STRING) /* extra blank */
  891.         r += TXT_WD; 
  892.     bar(l, t, r, b);
  893.     outtextxy(l+1, t+1, sbuf);
  894.     switch (fp->type) {
  895.     case RADIO:
  896.         if (fp->radio) 
  897.             invert(l + fp->radio * TXT_WD, t, 
  898.                    l + fp->size * TXT_WD, b);
  899.         break;
  900.     case BUTTON: case EXIT:
  901.         if (*fp->data) invert(l+TXT_WD, t, r-TXT_WD, b);
  902.         break;
  903.     }
  904. }
  905.  
  906. /* move the item's new data back to the application */
  907.  
  908. static
  909. item_update(buf, fp) char *buf; Form *fp; {
  910.     switch (fp->type) {
  911.     case STRING:
  912.         strncpy(fp->data, buf, fp->size);
  913.         break;
  914.     case NUMBER:
  915.         *((int *)fp->data) = atoi(buf);
  916.         break;
  917.     }
  918. }
  919.  
  920. /* we had some trouble, complain and get out */
  921.  
  922. trouble(fmt, args) char *fmt; {
  923.     int mode = getgraphmode();
  924.     restorecrtmode();
  925.     vprintf(fmt, &args);
  926.     printf("\n type any key to continue..."); getch();
  927.     setgraphmode(mode);
  928. }
  929.  
  930. #ifdef DEBUG
  931. /* debugging routine */
  932.  
  933. message(x, y, fmt, a, b, c, d, e) char *fmt; {
  934.     static char buf[100];
  935.     sprintf(buf, fmt, a, b, c, d, e);
  936.     setfillstyle(EMPTY_FILL, 0);
  937.     bar(x, y, x+((strlen(buf)+1)*TXT_WD), y+TXT_HT);
  938.     setcolor(1);
  939.     outtextxy(x, y, buf);
  940. }
  941. #endif
  942.