home *** CD-ROM | disk | FTP | other *** search
/ Atari FTP / ATARI_FTP_0693.zip / ATARI_FTP_0693 / Mint / toswinsc.zoo / menu.c < prev    next >
C/C++ Source or Header  |  1992-10-27  |  15KB  |  721 lines

  1. /*
  2.  * Copyright 1992 Eric R. Smith. All rights reserved.
  3.  * Redistribution is permitted only if the distribution
  4.  * is not for profit, and only if all documentation
  5.  * (including, in particular, the file "copying")
  6.  * is included in the distribution in unmodified form.
  7.  * THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY, NOT
  8.  * EVEN THE IMPLIED WARRANTIES OF MERCHANTIBILITY OR
  9.  * FITNESS FOR A PARTICULAR PURPOSE. USE AT YOUR OWN
  10.  * RISK.
  11.  */
  12. #include <stdlib.h>
  13. #include <stddef.h>
  14. #include <string.h>
  15. #include <osbind.h>
  16. #include <ctype.h>
  17. #include <keycodes.h>
  18. #include "xgem.h"
  19.  
  20. char *about_string = "About...";
  21. char *desk_string = "TOSWIN";
  22. void (*about_func)() = 0;
  23.  
  24. /* local variables */
  25. static MENU     *curmenu = 0;    /* currently displayed menu */
  26. static MENU    *deskmenu = 0;    /* the whole menu bar, include " Desk " */
  27. static OBJECT    *menuobj = 0;    /* menu object for the displayed menu */
  28.  
  29. /*
  30.  * Create a menu with the given title. Under GEM, we automatically
  31.  * add a space before and after the title, for aesthetics. (This is
  32.  * done in fixmenu.)
  33.  */
  34.  
  35. MENU *
  36. create_menu(title)
  37.     const char *title;
  38. {
  39.     MENU *m;
  40.  
  41.     m = malloc(sizeof(MENU));
  42.     if (!m) return m;
  43.     m->next = 0;
  44.     m->title = strdup(title);
  45.     m->width = 0;
  46.     m->contents = 0;
  47.     m->index = 0;
  48.     return m;
  49. }
  50.  
  51. /*
  52.  * Destroy a menu, freeing any memory allocated for it.
  53.  */
  54.  
  55. void
  56. destroy_menu(menu)
  57.     MENU *menu;
  58. {
  59.     MENU *m;
  60.     ENTRY *e;
  61.  
  62.     if (curmenu && menu == curmenu) {
  63.         hide_menu();
  64.     }
  65.  
  66.     while (menu) {
  67.         m = menu; menu = m->next;
  68.         while ((e = m->contents)) {
  69.             m->contents = e->next;
  70.             free(e->entry);
  71.             free(e);
  72.         }
  73.         free(m);
  74.     }
  75. }
  76.  
  77. /*
  78.  * Add a new entry to a menu. We automatically add 2 spaces before and
  79.  * 2 after the menu entry string, for aesthetics, in fixmenu, and
  80.  * any special key symbols are also placed there; so the user doesn't
  81.  * need to worry about these details.
  82.  * NOTE: the string given as `entry' need not remain around permanently;
  83.  * we duplicate it just to be on the safe side.
  84.  * Returns: the newly added entry, or NULL on failure.
  85.  */
  86.  
  87. ENTRY *
  88. add_entry( m, entry, f, arg, key, state)
  89.     MENU *m;
  90.     char *entry;
  91.     void (*f)();
  92.     void *arg;
  93.     int key, state;
  94. {
  95.     ENTRY *e, **ep;
  96.  
  97.     e = malloc(sizeof(ENTRY));
  98.     if (!e) return 0;
  99.     ep = &m->contents;
  100.     while (*ep) {
  101.         ep = &((*ep)->next);
  102.     }
  103.     *ep = e;
  104.     e->next = 0;
  105.     e->entry = strdup(entry);
  106.     e->func = f;
  107.     e->arg = arg;
  108.     e->state = state;
  109.     e->keycode = key;
  110.     e->index = 0;
  111.     return e;
  112. }
  113.  
  114. /* calculate the length of an entry; this is:
  115.  * 2 (leading blanks) + strlen(e->entry) + 2 (trailing blanks) +
  116.  * (optional) some number of bytes for any key symbols
  117.  */
  118.  
  119. #define MAXKEYSYMLEN 4
  120.  
  121. int
  122. entrylen(e)
  123.     ENTRY *e;
  124. {
  125.     return 4 + strlen(e->entry) + (e->keycode ? 1 + strlen(UNALT(e->keycode)) :
  126.                      0);
  127. }
  128.  
  129. /*
  130.  * make a string representing a menu entry
  131.  */
  132.  
  133. char *
  134. entrystr(e, wide)
  135.     ENTRY *e;
  136.     int wide;
  137. {
  138.     int i, n;
  139.     char *s, *ret, c;
  140.  
  141.     n = wide + 1;
  142.     ret = malloc(n);
  143.     if (!ret) return 0;
  144.     i = 2;
  145.     c = '-';
  146.     for (s = e->entry; *s; s++) {
  147.         ret[i++] = *s;
  148.         if (*s != '-') c = ' ';
  149.         if (i == n) break;
  150.     }
  151.     ret[0] = c; ret[1] = c;            /* leading blanks or dashes */
  152.  
  153.     while (i < n-1)
  154.         ret[i++] = c;            /* trailing blanks or dashes */
  155.  
  156.     ret[n-1] = 0;
  157. /* special case for '-----' type strings */
  158.     if (c == '-') {
  159.         return ret;
  160.     }
  161.  
  162. /* special symbols for keyboard equivalents */
  163.     if (e->keycode) {
  164.         s = UNALT(e->keycode);
  165.         i = (n-2) - strlen(s);        /* right justify, trailing blank */
  166.         if (i > 3) {
  167.             while (*s)
  168.                 ret[i++] = *s++;
  169.         }
  170.     }
  171.     return ret;
  172. }
  173.  
  174. static OBJECT *
  175. fixmenu( bar )
  176.     MENU *bar;
  177. {
  178.     OBJECT *obj;
  179.     int i, wide, y, place;
  180.     int numobjects, num_titles, num_entries;
  181.     int menubox;
  182.     MENU *m; ENTRY *e;
  183.     char *s;
  184.  
  185.     numobjects = 4;    /* all menu bars needs some invisible boxes */
  186.     num_titles = 0;
  187.  
  188. /* count up the number of objects necessary */
  189.     for (m = bar; m; m = m->next) {
  190.         num_titles++;
  191.         numobjects++;        /* for the title */
  192.         numobjects++;        /* for the menu box */
  193.         for (e = m->contents; e; e = e->next) {
  194.             numobjects++;    /* for the entry */
  195.         }
  196.     }
  197.  
  198.     obj = malloc(numobjects * sizeof(OBJECT));
  199.     if (!obj) return obj;
  200.  
  201. /* now we create the various objects we need */
  202. /* first, the root menu bar object */
  203.     obj[0].ob_next = -1;
  204.     obj[0].ob_head = 1;
  205.     obj[0].ob_tail = num_titles + 3;
  206.     obj[0].ob_type = G_IBOX;
  207.     obj[0].ob_flags = NONE; obj[0].ob_state = NORMAL;
  208.     obj[0].ob_spec = 0L;
  209.     obj[0].ob_x = 0; obj[0].ob_y = 0;
  210.     obj[0].ob_width = 90; obj[0].ob_height = 25;
  211.  
  212. /* now the menu bar box itself */
  213.     obj[1].ob_next = obj[0].ob_tail;
  214.     obj[1].ob_head = 2;
  215.     obj[1].ob_tail = 2;
  216.     obj[1].ob_type = G_BOX;
  217.     obj[1].ob_flags = NONE; obj[1].ob_state = NORMAL;
  218.     obj[1].ob_spec = 0x00001100L;
  219.     obj[1].ob_x = 0; obj[1].ob_y = 0;
  220.     obj[1].ob_width = 90; obj[1].ob_height = 513;
  221.  
  222.     obj[2].ob_next = 1;
  223.     obj[2].ob_head = 3;
  224.     obj[2].ob_tail = 2 + num_titles;
  225.     obj[2].ob_type = G_IBOX;
  226.     obj[2].ob_flags = NONE; obj[2].ob_state = NORMAL;
  227.     obj[2].ob_spec = 0L;
  228.     obj[2].ob_x = 2; obj[2].ob_y = 0;
  229.     obj[2].ob_width = 0;        /* will be adjusted later */
  230.     obj[2].ob_height = 769;
  231.  
  232.     i = 3;
  233.     for (m = bar; m; m = m->next) {
  234.         m->index = i;
  235.         obj[i].ob_next = (m->next  == 0) ? 2 : i+1;
  236.         obj[i].ob_head = obj[i].ob_tail = -1;
  237.         obj[i].ob_type = G_TITLE;
  238.         obj[i].ob_flags = NONE; obj[i].ob_state = NORMAL;
  239.         obj[i].ob_width = strlen(m->title) + 2;
  240.         s = malloc(obj[i].ob_width + 1);
  241.         if (!s) return NULL;
  242.         s[0] = ' ';
  243.         strcpy(s+1, m->title);
  244.         strcat(s, " ");
  245.         obj[i].ob_spec = (long)s;
  246.         obj[i].ob_height = 769;
  247.         obj[i].ob_x = obj[2].ob_width;
  248.         obj[2].ob_width += obj[i].ob_width;
  249.         obj[i].ob_y = 0;
  250.         i++;
  251.     }
  252.     obj[i].ob_next = 0;
  253.     obj[i].ob_head = i+1;
  254.     obj[i].ob_tail = 0;        /* to be adjusted later */
  255.     menubox = i;
  256.     obj[i].ob_type = G_IBOX;
  257.     obj[i].ob_flags = NONE; obj[i].ob_state = NORMAL;
  258.     obj[i].ob_spec = 0L;
  259.     obj[i].ob_x = 0; obj[i].ob_y = 769;
  260.     obj[i].ob_width = 80; obj[i].ob_height = 19;
  261.     i++;
  262.  
  263. /* now, for each menu we calculate the number of entries and
  264.  * the size of the necessary box
  265.  */
  266.     place = 2;
  267.  
  268.     for (m = bar; m; m = m->next) {
  269.         int box;
  270.  
  271.         box = i;
  272.         num_entries = wide = 0;
  273.         for(e = m->contents; e; e = e->next) {
  274.             num_entries++;
  275.             if (m == deskmenu) {
  276.                 if (strlen(e->entry) > wide)
  277.                     wide = strlen(e->entry);
  278.             } else {
  279.                 if (entrylen(e) > wide)
  280.                     wide = entrylen(e);
  281.             }
  282.         }
  283.         if (m->next)
  284.             obj[i].ob_next = i + num_entries + 1;
  285.         else {
  286.             obj[i].ob_next = menubox;
  287.             obj[menubox].ob_tail = i;
  288.         }
  289.         obj[i].ob_head = i+1;
  290.         obj[i].ob_tail = i + num_entries;
  291.         obj[i].ob_type = G_BOX;
  292.         obj[i].ob_flags = NONE; obj[i].ob_state = NORMAL;
  293.         obj[i].ob_spec = 0x00ff1100L;
  294.         obj[i].ob_x = place; obj[i].ob_y = 0;
  295.         place += strlen(m->title)+2;
  296.         obj[i].ob_width = wide; obj[i].ob_height = num_entries;
  297.         i++;
  298.         y = 0;
  299.  
  300.         for (e = m->contents; e; e = e->next) {
  301.             e->index = i;
  302.             obj[i].ob_next = (e->next) ? i+1 : box;
  303.             obj[i].ob_head = obj[i].ob_tail = -1;
  304.             obj[i].ob_type = G_STRING;
  305.             obj[i].ob_flags = NONE;
  306.             obj[i].ob_state = e->state;
  307.     /* Do NOT malloc the strings for the Desk menu, or else there
  308.      * will be a memory leak!
  309.      */
  310.             if (m != deskmenu)
  311.                 s = entrystr(e, wide);
  312.             else {
  313.                 s = e->entry;
  314.             }
  315.             if (!s) return NULL;
  316.             obj[i].ob_spec = (long)s;
  317.             obj[i].ob_x = 0; obj[i].ob_y = y++;
  318.             obj[i].ob_width = wide; obj[i].ob_height = 1;
  319.             i++;
  320.         }
  321.     }
  322.     obj[i-1].ob_flags = LASTOB;
  323.  
  324. /* now, fix the object tree up */
  325.     for (i = 0; i < numobjects; i++)
  326.         rsrc_obfix(obj, i);
  327.  
  328.     return obj;
  329. }
  330.  
  331. void
  332. handle_menu(title, index)
  333.     int title, index;
  334. {
  335.     MENU *m;
  336.     ENTRY *e;
  337.  
  338.     for (m = deskmenu; m; m = m->next) {
  339.         if (m->index == title) break;
  340.     }
  341.     if (m) {
  342.         for (e = m->contents; e; e = e->next) {
  343.         if (e->index == index) {
  344.             (*e->func)(e->arg);
  345.             menu_tnormal(menuobj, title, 1);
  346.             return;
  347.         }
  348.         }
  349.     }
  350.  
  351. /* strange. Let's just punt and return */
  352. }
  353.  
  354. /*
  355.  * erase menu currently on screen (if one exists), and frees the memory
  356.  * allocated for its strings
  357.  */
  358.  
  359. void
  360. hide_menu()
  361. {
  362.     int i;
  363.     int firststring;
  364.     extern short _app;
  365.  
  366.     if (!_app) {
  367.         if (deskmenu)
  368.             deskmenu->next = curmenu = 0;
  369.         return;
  370.     }
  371.  
  372.     if (curmenu && curmenu->contents) {
  373.         firststring = curmenu->contents->index;
  374.     } else {
  375.         firststring = 0x7fff;    /* ridiculously big number */
  376.     }
  377.  
  378.     if (menuobj) {
  379.         menu_bar(menuobj, 0);
  380.         i = 0;
  381.         for(;;) {
  382.             if (menuobj[i].ob_type == G_TITLE)
  383.                 free((void *)menuobj[i].ob_spec);
  384.             else if (menuobj[i].ob_type == G_STRING) {
  385.     /* Something to watch out for here: the desktop will replace the 6
  386.      * .ACC strings with its own strings (eek). So, we can only
  387.      * free G_STRINGS with the "right" indices
  388.      */
  389.                 if (i >= firststring)
  390.                     free((void *)menuobj[i].ob_spec);
  391.             }
  392.             if (menuobj[i].ob_flags & LASTOB) break;
  393.             i++;
  394.         }
  395.         free(menuobj);
  396.         menuobj = 0;
  397.         deskmenu->next = curmenu = 0;
  398.     }
  399. }
  400.  
  401. /*
  402.  * Show a menu bar; this will erase any previous menu. It will also
  403.  * automatically construct a "Desk" menu.
  404.  */
  405.  
  406. void
  407. show_menu( m )
  408.     MENU *m;
  409. {
  410.     static void dflt_about(), nullfunc();
  411.     static char about_buf[22] = "  Your message here";
  412.     int i; char *s;
  413.     extern short _app;
  414.  
  415.     hide_menu();
  416.  
  417.     if (deskmenu == 0) {
  418.         deskmenu = create_menu(desk_string);
  419.         add_entry(deskmenu, about_buf, dflt_about, NULL, 0, NORMAL);
  420.         add_entry(deskmenu, "--------------------", nullfunc, NULL, 0,
  421.             DISABLED);
  422.         add_entry(deskmenu, "1", nullfunc, NULL, 0, NORMAL);
  423.         add_entry(deskmenu, "2", nullfunc, NULL, 0, NORMAL);
  424.         add_entry(deskmenu, "3", nullfunc, NULL, 0, NORMAL);
  425.         add_entry(deskmenu, "4", nullfunc, NULL, 0, NORMAL);
  426.         add_entry(deskmenu, "5", nullfunc, NULL, 0, NORMAL);
  427.         add_entry(deskmenu, "6", nullfunc, NULL, 0, NORMAL);
  428.     }
  429.  
  430.     s = about_string;
  431.     for (i = 2; i < 20; i++) {
  432.         if (*s) {
  433.             about_buf[i] = *s++;
  434.         } else {
  435.             about_buf[i] = ' ';
  436.         }
  437.     }
  438.  
  439.     deskmenu->contents->entry = about_buf;
  440.     deskmenu->contents->func = about_func ? about_func : dflt_about;
  441.  
  442.     deskmenu->next = curmenu = m;
  443.  
  444.     if (_app && (menuobj = fixmenu(deskmenu)) != NULL) {
  445.         menu_bar(menuobj, 1);
  446.     }
  447. }
  448.  
  449. /*
  450.  * Look for a keyboard equivalent for a given menu; if found,
  451.  * execute the associated action and return 1, otherwise return 0.
  452.  */
  453.  
  454. int
  455. menu_key(code, shift)
  456.     int code;    /* keyboard code we're looking for */
  457.     int shift;    /* not used */
  458. {
  459.     MENU *m;
  460.     ENTRY *e;
  461.     extern short _app;
  462.  
  463.     for (m = curmenu; m; m = m->next) {
  464.         for (e = m->contents; e; e = e->next) {
  465.             if (e->keycode == code) {
  466.                 if (_app) {
  467.                     menu_tnormal(menuobj, m->index, 0);
  468.                     handle_menu(m->index, e->index);
  469.                 } else {
  470.                     (*e->func)(e->arg);
  471.                 }
  472.                 return 1;
  473.             }
  474.         }
  475.     }
  476.     return 0;
  477. }
  478.  
  479. static void
  480. nullfunc()
  481. {
  482. }
  483.  
  484. /* not actually needed, TOSWIN sets up an about() function properly */
  485.  
  486. static void
  487. dflt_about()
  488. {
  489. }
  490.  
  491. /*
  492.  * routines for checking/unchecking menu entries
  493.  */
  494.  
  495. void
  496. check_entry(m, e)
  497.     MENU *m;
  498.     ENTRY *e;
  499. {
  500.     MENU *n;
  501.  
  502.     e->state = CHECKED;
  503.     if (curmenu && menuobj) {
  504.         for (n = curmenu; n; n = n->next) {
  505.             if (n == m) {
  506.                 menu_icheck(menuobj, e->index, 1);
  507.                 return;
  508.             }
  509.         }
  510.     }
  511. }
  512.  
  513. void
  514. uncheck_entry(m, e)
  515.     MENU *m;
  516.     ENTRY *e;
  517. {
  518.     MENU *n;
  519.  
  520.     e->state = NORMAL;
  521.     if (curmenu && menuobj) {
  522.         for (n = curmenu; n; n = n->next) {
  523.             if (n == m) {
  524.                 menu_icheck(menuobj, e->index, 0);
  525.                 return;
  526.             }
  527.         }
  528.     }
  529. }
  530.  
  531. /*
  532.  * routines for enabling/disabling menu items
  533.  */
  534.  
  535. void
  536. enable_entry(m, e)
  537.     MENU *m;
  538.     ENTRY *e;
  539. {
  540.     MENU *n;
  541.  
  542.     e->state = NORMAL;
  543.     if (curmenu && menuobj) {
  544.         for (n = curmenu; n; n = n->next) {
  545.             if (n == m) {
  546.                 menu_ienable(menuobj, e->index, 1);
  547.                 return;
  548.             }
  549.         }
  550.     }
  551. }
  552.  
  553. void
  554. disable_entry(m, e)
  555.     MENU *m;
  556.     ENTRY *e;
  557. {
  558.     MENU *n;
  559.  
  560.     e->state = DISABLED;
  561.     if (curmenu && menuobj) {
  562.         for (n = curmenu; n; n = n->next) {
  563.             if (n == m) {
  564.                 menu_ienable(menuobj, e->index, 0);
  565.                 return;
  566.             }
  567.         }
  568.     }
  569. }
  570.  
  571. /*
  572.  * ALT: finds the appropriate key-code for, e.g., ALT-A based upon
  573.  * the current settings of the keyboard caps lock table
  574.  */
  575.  
  576. int
  577. ALT(c)
  578.     int c;
  579. {
  580.     char *capstab;
  581.     int i;
  582.  
  583.     c = toupper(c);
  584.     if (c == '0') return (ALT_0 << 8);
  585.     if (c >= '1' && c <= '9') {
  586.         return (ALT_1 + (c - '1')) << 8;
  587.     }
  588.  
  589.     capstab = *( ((char **)Keytbl(-1L, -1L, -1L)) + 2 );
  590.     for (i = 0; i < 127; i++) {
  591.         if (capstab[i] == c)
  592.             return (i << 8);
  593.     }
  594.     return 0;
  595. }
  596.  
  597. /*
  598.  * UNALT: somewhat misnamed; finds an ASCII string representation for
  599.  * the given keycode. Note that the returned string is overwritten
  600.  * by subsequent UNALT calls.
  601.  */
  602.  
  603. #define ALTSYM '\007'
  604. #define CTRLSYM '^'
  605. #define SHIFTSYM '\001'
  606.  
  607. struct kdef {
  608.     int scan;
  609.     char *name;
  610. } keyname[] = {
  611.     K_INS, "Ins",
  612.     K_HOME, "Home",
  613.     K_UNDO, "Undo",
  614.     K_HELP, "Help",
  615.     CURS_UP, "\001",
  616.     CURS_DN, "\002",
  617.     CURS_RT, "\003",
  618.     CURS_LF, "\004",
  619.     0, 0
  620. };
  621.  
  622. char *
  623. UNALT(code)
  624.     int code;
  625. {
  626.     char *capstab, *s;
  627.     static char retbuf[8];
  628.     int scan, c;
  629.     struct kdef *k;
  630.  
  631.     s = retbuf;
  632.     if (!code) {
  633.         *s++ = 0;
  634.         return retbuf;
  635.     }
  636.  
  637.     c = code & 0x00ff;
  638.     scan = (code & 0xff00) >> 8;
  639.  
  640.     if ((scan == CURS_UP && c == '8') ||
  641.         (scan == CURS_DN && c == '2') ||
  642.         (scan == CURS_RT && c == '6') ||
  643.         (scan == CURS_LF && c == '4')) {
  644.         *s++ = 'S'; *s++ = 'h'; *s++ = 'f'; *s++ = 't';
  645.         c = 0;
  646.     } else if (scan == K_HOME && c == '7') {
  647.         strcpy(s, "Clr");
  648.         return retbuf;
  649.     }
  650.  
  651.     if (c) {
  652.         if (c == 0x7f) {    /* DEL */
  653.             *s++ = 'D';
  654.             *s++ = 'E';
  655.             c = 'L';
  656.         } else if (c == '\033') {    /* ESC */
  657.             *s++ = 'E';
  658.             *s++ = 'S';
  659.             c = 'C';
  660.         }
  661.         if (c < ' ') {        /* control characters */
  662.             *s++ = CTRLSYM;
  663.             c += '@';
  664.         }
  665.         *s++ = c;
  666.         *s++ = 0;
  667.         return retbuf;
  668.     }
  669.  
  670. /* Special cases go here... */
  671.     for (k = keyname; k->name; k++) {
  672.         if (scan == k->scan) {
  673.             strcpy(s, k->name);
  674.             return retbuf;
  675.         }
  676.     }
  677.  
  678. /* shifted function keys */
  679.     if (scan >= SHF_1 && scan <= SHF_10) {
  680.         scan = (scan - SHF_1) + F_10 + 1;
  681.         /* fall through */
  682.     }
  683.  
  684. /* function keys */
  685.     if (scan >= F_1 && scan <= F_10 + 10) {
  686.         *s++ = 'F';
  687.         c = scan - F_1 + 1;
  688.         if (c >= 10) {
  689.             *s++ = '1';
  690.             c -= 10;
  691.             if (c == 10) {
  692.                 s[-1] = '2';
  693.                 c = 0;
  694.             }
  695.         }
  696.         *s++ = '0'+c;
  697.         *s++ = 0;
  698.         return retbuf;
  699.     }
  700.  
  701. /* Now check for ALT keys */
  702.     if (scan >= ALT_1 && scan <= ALT_0) {
  703.         *s++ = ALTSYM;
  704.         scan = (scan - ALT_1) + 1;
  705.         if (scan == 10) scan = 0;
  706.         *s++ = '0'+scan;
  707.         *s++ = 0;
  708.         return retbuf;
  709.     }
  710.  
  711. /* look for ALT+key */
  712.     capstab = *( ((char **)Keytbl(-1L, -1L, -1L)) + 2 );
  713.     c = capstab[scan];
  714.     if (c >= 'A' && c <= 'Z') {
  715.         *s++ = ALTSYM;
  716.         *s++ = c;
  717.     }
  718.     *s++ = 0;
  719.     return retbuf;
  720. }
  721.