home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_11_08 / weber / menu.c < prev    next >
Text File  |  1993-03-13  |  32KB  |  879 lines

  1. /***************************************************************
  2.  * file: MENU.C
  3.  * purpose:
  4.  *  menubar functions for simple gui
  5.  * contains:
  6.  *  menu_open(MENU_ITEM menubar[],short number_of_items);   draws a menubar
  7.  *  menu_message_handler(MESSAGE *message,MENU_ITEM *menu); handles menu messages
  8.  *  menu_close(MENU_ITEM menubar[]);                        erases a menubar
  9.  *  menu_modify(MENU_ITEM menubar[],short menu_id,unsigned short status);   modifies the status of a menu item, ACTIVE, GRAY or HIDDEN
  10.  *  menu_clear(MENU_ITEM menubar[]);                        clear any open popups
  11.  *  popup_open(POPUP_ITEM popup[],short number_of_items);   draws a popup
  12.  *  popup_message_handler(MESSAGE *message,POPUP_ITEM *popup);  handles popup messages
  13.  *  popup_close(POPUP_ITEM popup[]);                        erases a popup
  14.  *  popup_modify(POPUP_ITEM popup[],short popup_id,unsigned short status);  modifies the status of a popup item, ACTIVE, GRAY or HIDDEN
  15.  * system: Written for the flash graphics library in Zortech 3.0
  16.  * copyright: 1991 by David Weber.  All rights reserved.
  17.  *  This software can be used for any purpose as object, library or executable.
  18.  *  It cannot be sold for profit as source code.
  19.  * history:
  20.  *  12-22-91 - initial code
  21.  *  01-31-93 - this code is now obsolete, see the CPP gui package
  22.  **************************************************************/
  23.  
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include "gui.h"
  27.  
  28. /* local data */
  29. static MENU_ITEM *menu_item_is_selected = NULL;
  30. static MENU_ITEM *menu_last_seen = NULL;
  31.  
  32. /* local prototypes */
  33. static void selection_focus(short x1,short y1,short x2,short y2);
  34. static void selection_unfocus(short x1,short y1,short x2,short y2);
  35. static short menu_draw_text(short x,short y,char *str,unsigned short status);
  36. static short menu_clear_any_items(MENU_ITEM *menu);
  37. static void menu_select_item(MENU_ITEM *menu);
  38. static void menu_clear_item(MENU_ITEM *menu);
  39. static void menu_select_previous_item(MENU_ITEM *first,MENU_ITEM *current);
  40. static void menu_select_next_item(MENU_ITEM *first,MENU_ITEM *current);
  41. static void menu_default_message_handler(MESSAGE *message);
  42. static MENU_ITEM *menu_current(MENU_ITEM *first);
  43. static void menu_modify_draw(MENU_ITEM *menu);
  44. static short popup_from_menu(MENU_ITEM *first,MENU_ITEM *active);
  45. static void popup_clear_any_item(POPUP_ITEM *popup);
  46. static void popup_select_item(POPUP_ITEM *popup);
  47. static void popup_clear_item(POPUP_ITEM *popup);
  48. static void popup_select_previous_item(POPUP_ITEM *first,POPUP_ITEM *current);
  49. static void popup_select_next_item(POPUP_ITEM *first,POPUP_ITEM *current);
  50. static POPUP_ITEM *popup_current(POPUP_ITEM *first);
  51. static void popup_modify_draw(POPUP_ITEM *popup);
  52.  
  53.  
  54.  
  55. /************************************************
  56.  * function: short menu_open(MENU_ITEM menubar[],short number_of_items)
  57.  *  register a menubar, initialize the hotspots and draw it
  58.  * parameters: array of menu items in menu and number of items to expect
  59.  * returns: 1 opened or 0 if failed cuz of lack of resources or bad data
  60.  ************************************************/
  61. short menu_open(MENU_ITEM menubar[],short number_of_items)
  62.     {
  63.     fg_box_t menu_area;
  64.     short i,j,text_x,text_y,cell_height;
  65.     MENU_ITEM *m,*m2;
  66.     POPUP_ITEM *p;
  67.     MESSAGE error;
  68.  
  69.     i = 0;      /* verify parameters */
  70.     if (number_of_items < 0 || number_of_items > MENU_MAX_ITEMS)
  71.         i = 1;
  72.     else
  73.         for (j = 0 ; j < number_of_items ; j++)
  74.             {
  75.             m = &menubar[j];
  76.             if (m->name == NULL || (m->status & ~MENU_BITS) ||
  77.                 m->accelerator < KEY_MIN || m->accelerator > KEY_MAX ||
  78.                 m->number_of_popup_items < 0 || m->number_of_popup_items > POPUP_MAX_ITEMS)
  79.                 i = 1;
  80.             }
  81.     if (i)
  82.         {
  83.         error.id = gui_errno = M_INVALID_PARMS;
  84.         error.data.ptr_data = menu_open;
  85.         message_send(&error);
  86.         return 0;
  87.         }
  88.     fg_msm_hidecursor();
  89.     cell_height = gui_char_height + 6;
  90.     text_y = 3;
  91.     if (gui_char_height + gui_char_height/2 > cell_height)
  92.         {
  93.         cell_height = gui_char_height + gui_char_height/2;
  94.         text_y = gui_char_height/4;
  95.         }
  96.     menu_area[FG_X1] = fg.displaybox[FG_X1];    /* coordinates of menubar */
  97.     menu_area[FG_X2] = fg.displaybox[FG_X2];
  98.     menu_area[FG_Y1] = fg.displaybox[FG_Y2] - cell_height;
  99.     menu_area[FG_Y2] = fg.displaybox[FG_Y2];
  100.     fg_boxclip(fg.displaybox,menu_area,menu_area);
  101.     if (!object_add(OBJECT_MENU,(GENERIC_MESSAGE_HANDLER)menu_message_handler,menubar,menu_area))
  102.         {                                       /* add object to active list */
  103.         fg_msm_showcursor();
  104.         fg_flush();
  105.         return 0;
  106.         }
  107.     fg_fillbox(COLOR_MENU_BACKGROUND,FG_MODE_SET,~0,menu_area);     /* draw bar */
  108.     fg_drawbox(COLOR_MENU_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,menu_area,fg.displaybox);
  109.     text_x = fg.displaybox[FG_X1] + 2 * gui_char_width;
  110.     text_y += menu_area[FG_Y1];
  111.     for (i = 0, m2 = NULL ; i < number_of_items ; i++)
  112.         {
  113.         m = &menubar[i];
  114.         m->screen[FG_X1] = text_x-2*gui_char_width;     /* locate hotspots */
  115.         m->screen[FG_Y1] = menu_area[FG_Y1];
  116.         m->screen[FG_Y2] = menu_area[FG_Y2];
  117.         text_x = menu_draw_text(text_x,text_y,m->name,m->status);   /* draw text */
  118.         m->screen[FG_X2] = text_x + 2 * gui_char_width;
  119.         text_x += 4 * gui_char_width;
  120.         if (m->popup != NULL)
  121.             {                               /* link popup items */
  122.             for (j = 0, p = m->popup ; j < m->number_of_popup_items-1 ; j++, p++)
  123.                 p->next = p + 1;
  124.             p->next = NULL;
  125.             }
  126.         m->next = NULL;                     /* link menu items */
  127.         if (m2 != NULL)
  128.             m2->next = m;
  129.         m2 = m;
  130.         }
  131.     fg_msm_showcursor();
  132.     fg_flush();
  133.     return 1;
  134.     }
  135.  
  136.  
  137. /************************************************
  138.  * function: void menu_message_handler(MESSAGE *message,MENU_ITEM *menu)
  139.  *  handles messages for menu objects
  140.  * parameters: pointer to message, pointer to object data
  141.  * returns: nothing
  142.  ************************************************/
  143. void menu_message_handler(MESSAGE *message,MENU_ITEM *menu)
  144.     {
  145.     MENU_ITEM *m;
  146.     POPUP_ITEM *p;
  147.     short key,x,y;
  148.  
  149.     menu_item_is_selected = NULL;
  150.     switch (message->id)
  151.         {
  152.         case M_KEY:
  153.             key = message->data.short_data.x;
  154.             input_handler_set_default(menu_default_message_handler);
  155.             menu_last_seen = menu;
  156.             for (m = menu ; m != NULL ; m = m->next)
  157.                 {
  158.                 if (m->status & MENU_SELECTED)
  159.                     {                           /* if menu item selected */
  160.                     menu_item_is_selected = m;      /* found a selected item */
  161.                     if (key == RETURN && m->popup == NULL)
  162.                         {                       /* select current item */
  163.                         message->id = m->id;
  164.                         return;
  165.                         }
  166.                     if (key == LEFTARROW)
  167.                         {                       /* select previous item */
  168.                         menu_select_previous_item(menu,m);
  169.                         message->id = M_NONE;
  170.                         return;
  171.                         }
  172.                     if (key == RIGHTARROW)
  173.                         {                       /* select next menu item */
  174.                         menu_select_next_item(menu,m);
  175.                         message->id = M_NONE;
  176.                         return;
  177.                         }
  178.                     if (key == SHIFTTAB)
  179.                         {                       /* select previous popup or menu */
  180.                         if (m->popup == NULL || popup_current(m->popup) == m->popup)
  181.                             {
  182.                             menu_select_previous_item(menu,m);
  183.                             m = menu_current(menu);
  184.                             }
  185.                         message->data.short_data.x = UPARROW;
  186.                         message_send_object(message,(void *) m->popup);
  187.                         message->id = M_NONE;
  188.                         return;
  189.                         }
  190.                     if (key == TAB)
  191.                         {                       /* select next popup or menu item */
  192.                         if (m->popup == NULL || popup_current(m->popup)->next == NULL)
  193.                             menu_select_next_item(menu,m);
  194.                         else
  195.                             {
  196.                             message->data.short_data.x = DOWNARROW;
  197.                             message_send_object(message,(void *) m->popup);
  198.                             }
  199.                         message->id = M_NONE;
  200.                         return;
  201.                         }
  202.                     }
  203.                 if (m->status & MENU_ACTIVE && m->accelerator == key)
  204.                     {                           /* found an accelerator key */
  205.                     if (m->popup != NULL)
  206.                         popup_from_menu(menu,m);
  207.                     else
  208.                         {
  209.                         menu_clear_any_items(menu);
  210.                         menu_select_item(m);
  211.                         }
  212.                     message->id = m->id;
  213.                     return;
  214.                     }
  215.                 if (m->popup != NULL)
  216.                     {           /* check accelerators on popup attached to menubar item */
  217.                     for (p = m->popup ; p != NULL ; p = p->next)
  218.                         if (p->status & MENU_ACTIVE && p->accelerator == key)
  219.                             {
  220.                             if ((m->status & MENU_SELECTED) == 0)
  221.                                 popup_from_menu(menu,m);
  222.                             if ((p->status & MENU_SELECTED) == 0)
  223.                                 {
  224.                                 popup_clear_any_item(m->popup);
  225.                                 popup_select_item(p);
  226.                                 }
  227.                             message->id = p->id;
  228.                             return;
  229.                             }
  230.                     }
  231.                 }
  232.             break;
  233.         case M_MOUSE_LEFT:
  234.         case M_MOUSE_CENTER:
  235.         case M_MOUSE_RIGHT:
  236.             x = message->data.short_data.x;
  237.             y = message->data.short_data.y;
  238.             for (m = menu ; m != NULL ; m = m->next)
  239.                 {
  240.                 if (m->status & MENU_SELECTED)
  241.                     {
  242.                     input_handler_set_default(menu_default_message_handler);
  243.                     menu_item_is_selected = m;      /* found a selected item */
  244.                     }
  245.                 if (fg_pt_inbox(m->screen,x,y))
  246.                     {                   /* menu item selected */
  247.                     if (m->status & MENU_ACTIVE)
  248.                         {
  249.                         if (m->popup != NULL)
  250.                             popup_from_menu(menu,m);
  251.                         else
  252.                             {
  253.                             menu_clear_any_items(menu);
  254.                             menu_select_item(m);
  255.                             }
  256.                         message->id = m->id;
  257.                         return;
  258.                         }
  259.                     message->id = M_NONE;
  260.                     return;
  261.                     }
  262.                 }
  263.             break;
  264.         default:
  265.             return;
  266.         }
  267.     }
  268.  
  269.  
  270. /************************************************
  271.  * function: short menu_close(MENU_ITEM menubar[])
  272.  * parameters: pointer to previously opened menubar
  273.  * returns: 1 if closed or 0 if failed
  274.  ************************************************/
  275. short menu_close(MENU_ITEM menubar[])
  276.     {
  277.     short ret;
  278.     MENU_ITEM *m;
  279.  
  280.     fg_msm_hidecursor();
  281.     if ((ret = object_remove(menubar)) != 0)
  282.         for (m = menubar ; m != NULL ; m = m->next)
  283.             m->status &= ~MENU_SELECTED;
  284.     fg_msm_showcursor();
  285.     fg_flush();
  286.     return ret;
  287.     }
  288.  
  289.  
  290. /************************************************
  291.  * function: short menu_modify(MENU_ITEM menubar[],short menu_id,unsigned short status)
  292.  *      set the status of an element in a previously opened menubar or associated
  293.  *      popup and redraw it
  294.  *      This will set only MENU_ACTIVE, MENU_GRAY or MENU_HIDDEN
  295.  * parameters: menu array, id of menu or popup element to set, new status value
  296.  * returns: 1 if set or 0 if not opened or improper id
  297.  ************************************************/
  298. short menu_modify(MENU_ITEM menubar[],short menu_id,unsigned short status)
  299.     {
  300.     MENU_ITEM *m;
  301.     POPUP_ITEM *p;
  302.  
  303.     if (object_exists(menubar) == NULL)
  304.         return 0;
  305.     for (m = menubar ; m != NULL ; m = m->next)
  306.         {
  307.         if (m->id == menu_id)       /* check menu */
  308.             {
  309.             m->status = (m->status & ~MENU_STATUS_MASK) | (status & MENU_STATUS_MASK);
  310.             menu_modify_draw(m);
  311.             if (m->status & MENU_SELECTED)
  312.                 if ((m->status & MENU_ACTIVE) == 0)
  313.                     menu_select_next_item(menubar,m);
  314.             return 1;
  315.             }
  316.         for (p = m->popup ; p != NULL ; p = p->next)    /* check attached popups */
  317.             if (p->id == menu_id)
  318.                 {
  319.                 p->status = (p->status & ~MENU_STATUS_MASK) | (status & MENU_STATUS_MASK);
  320.                 popup_modify_draw(p);
  321.                 if (p->status & MENU_SELECTED)
  322.                     if ((p->status & MENU_ACTIVE) == 0)
  323.                         popup_select_next_item(m->popup,p);
  324.                 return 1;
  325.                 }
  326.         }
  327.     return 0;
  328.     }
  329.  
  330.  
  331. /************************************************
  332.  * function: short menu_clear(MENU_ITEM menubar[])
  333.  * parameters: pointer to previously opened menubar
  334.  * returns: 1 if cleared or 0 if failed
  335.  ************************************************/
  336. short menu_clear(MENU_ITEM menubar[])
  337.     {
  338.     if (object_exists(menubar) == NULL)
  339.         return 0;
  340.     menu_clear_any_items(menubar);
  341.     return 1;
  342.     }
  343.  
  344.  
  345. /************************************************
  346.  * function: short popup_open(POPUP_ITEM popup[],short number_of_items,short x,short y)
  347.  *  register a popup, initialize the hotspots and draw it
  348.  * parameters: array of popup items in popup, number of items to expect and screen location
  349.  * returns: 1 opened or 0 if failed cuz of lack of resources
  350.  ************************************************/
  351. short popup_open(POPUP_ITEM popup[],short number_of_items,short x,short y)
  352.     {
  353.     fg_box_t popup_area;
  354.     short i,j,text_y,right_x,cell_height;
  355.     POPUP_ITEM *p,*p2;
  356.     MESSAGE error;
  357.  
  358.     i = 0;      /* verify parameters */
  359.     if (number_of_items < 0 || number_of_items > POPUP_MAX_ITEMS)
  360.         i = 1;
  361.     else
  362.         for (j = 0 ; j < number_of_items ; j++)
  363.             {
  364.             p = &popup[j];
  365.             if (p->name == NULL || (p->status & ~MENU_BITS) ||
  366.                 p->accelerator < KEY_MIN || p->accelerator > KEY_MAX)
  367.                 i = 1;
  368.             }
  369.     if (i)
  370.         {
  371.         error.id = gui_errno = M_INVALID_PARMS;
  372.         error.data.ptr_data = menu_open;
  373.         message_send(&error);
  374.         return 0;
  375.         }
  376.     fg_msm_hidecursor();
  377.     cell_height = gui_char_height + 6;
  378.     popup_area[FG_X1] = x;                      /* coordinates of popup */
  379.     popup_area[FG_Y1] = y - number_of_items * cell_height;
  380.     popup_area[FG_X2] = x;
  381.     popup_area[FG_Y2] = y;
  382.     for (i = 0 ; i < number_of_items ; i++)
  383.         {                                       /* maximum string width */
  384.         right_x = x + (strlen(popup[i].name)+2) * gui_char_width;
  385.         if (right_x > popup_area[FG_X2])
  386.             popup_area[FG_X2] = right_x;
  387.         }
  388.     fg_boxclip(fg.displaybox,popup_area,popup_area);
  389.     if (!object_add(OBJECT_POPUP,(GENERIC_MESSAGE_HANDLER)popup_message_handler,popup,popup_area))
  390.         {                                       /* add to active object list */
  391.         fg_msm_showcursor();
  392.         fg_flush();
  393.         return 0;
  394.         }
  395.     fg_fillbox(COLOR_MENU_BACKGROUND,FG_MODE_SET,~0,popup_area);        /* draw popup */
  396.     fg_drawbox(COLOR_MENU_FOREGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,popup_area,fg.displaybox);
  397.     text_y = y + (cell_height-gui_char_height)/2;
  398.     for (i = 0, p2 = NULL ; i < number_of_items ; i++)
  399.         {
  400.         p = &popup[i];
  401.         p->screen[FG_X1] = x;   /* locate hotspots */
  402.         p->screen[FG_X2] = right_x;
  403.         p->screen[FG_Y2] = y;
  404.         text_y -= cell_height;
  405.         y -= cell_height;
  406.         p->screen[FG_Y1] = y;
  407.         (void) menu_draw_text(x+gui_char_width,text_y,p->name,p->status);   /* draw text */
  408.         p->next = NULL;         /* link popups */
  409.         if (p2 != NULL)
  410.             p2->next = p;
  411.         p2 = p;
  412.         }
  413.     fg_msm_showcursor();
  414.     fg_flush();
  415.     return 1;
  416.     }
  417.  
  418.  
  419. /************************************************
  420.  * function: void popup_message_handler(MESSAGE *message,POPUP_ITEM *popup)
  421.  *  handles messages for popup objects
  422.  * parameters: pointer to message, pointer to object data
  423.  * returns: nothing
  424.  ************************************************/
  425. void popup_message_handler(MESSAGE *message,POPUP_ITEM *popup)
  426.     {
  427.     POPUP_ITEM *p;
  428.     short key,x,y;
  429.  
  430.     switch (message->id)
  431.         {
  432.         case M_KEY:
  433.             key = message->data.short_data.x;
  434.             for (p = popup ; p != NULL ; p = p->next)
  435.                 {
  436.                 if (p->status & MENU_SELECTED)  /* if popup is open and selected */
  437.                     {
  438.                     if (key == DOWNARROW)       /* move to next item */
  439.                         {
  440.                         popup_select_next_item(popup,p);
  441.                         message->id = M_NONE;
  442.                         return;
  443.                         }
  444.                     if (key == UPARROW)         /* move to previous item */
  445.                         {
  446.                         popup_select_previous_item(popup,p);
  447.                         message->id = M_NONE;
  448.                         return;
  449.                         }
  450.                     if (key == RETURN)          /* current item selected */
  451.                         {
  452.                         message->id = p->id;
  453.                         return;
  454.                         }
  455.                     }
  456.                 if (p->status & MENU_ACTIVE && p->accelerator == key)
  457.                     {
  458.                     popup_clear_any_item(popup);
  459.                     popup_select_item(p);
  460.                     message->id = p->id;        /* accelerator key selected */
  461.                     return;
  462.                     }
  463.                 }
  464.             break;
  465.         case M_MOUSE_LEFT:
  466.         case M_MOUSE_CENTER:
  467.         case M_MOUSE_RIGHT:
  468.             x = message->data.short_data.x;
  469.             y = message->data.short_data.y;
  470.             for (p = popup ; p != NULL ; p = p->next)
  471.                 if (fg_pt_inbox(p->screen,x,y))
  472.                     {                       /* mouse clicked on item in an open popup */
  473.                     if (p->status & MENU_ACTIVE)
  474.                         {
  475.                         popup_clear_any_item(popup);
  476.                         popup_select_item(p);
  477.                         message->id = p->id;
  478.                         return;
  479.                         }
  480.                     message->id = M_NONE;
  481.                     return;
  482.                     }
  483.             break;
  484.         default:
  485.             return;
  486.         }
  487.     }
  488.  
  489.  
  490. /************************************************
  491.  * function: short popup_close(POPUP_ITEM popup[])
  492.  * parameters: pointer to previously opened popup
  493.  * returns: 1 if closed or 0 if failed
  494.  ************************************************/
  495. short popup_close(POPUP_ITEM popup[])
  496.     {
  497.     short ret;
  498.     POPUP_ITEM *p;
  499.  
  500.     fg_msm_hidecursor();
  501.     if ((ret = object_remove(popup)) != 0)
  502.         for (p = popup ; p != NULL ; p = p->next)
  503.             p->status &= ~MENU_SELECTED;
  504.     fg_msm_showcursor();
  505.     fg_flush();
  506.     return ret;
  507.     }
  508.  
  509.  
  510. /************************************************
  511.  * function: short popup_modify(POPUP_ITEM popup[],short popup_id,unsigned short status)
  512.  *      set the status of an element in a previously opened popup and redraw it
  513.  *      This will set only MENU_ACTIVE, MENU_GRAY or MENU_HIDDEN
  514.  * parameters: popup array, id of element to set, new status value
  515.  * returns: 1 if set or 0 if not opened or improper id
  516.  ************************************************/
  517. short popup_modify(POPUP_ITEM popup[],short popup_id,unsigned short status)
  518.     {
  519.     POPUP_ITEM *p;
  520.  
  521.     if (object_exists(popup) == NULL)
  522.         return 0;
  523.     for (p = popup ; p != NULL ; p = p->next)
  524.         if (p->id == popup_id)
  525.             {
  526.             p->status = (p->status & ~MENU_STATUS_MASK) | (status & MENU_STATUS_MASK);
  527.             popup_modify_draw(p);
  528.             if (p->status & MENU_SELECTED)
  529.                 if ((p->status & MENU_ACTIVE) == 0)
  530.                     popup_select_next_item(popup,p);
  531.             return 1;
  532.             }
  533.     return 0;
  534.     }
  535.  
  536.  
  537. /* ---------------- LOCAL FUNCTIONS ---------------- */
  538.  
  539. /* highlight a menu/popup selection */
  540. static void selection_focus(short x1,short y1,short x2,short y2)
  541.     {
  542.     fg_box_t selection;
  543.  
  544.     fg_msm_hidecursor();
  545.     fg_make_box(selection,x1,y1,x2,y2);
  546.     fg_drawbox(COLOR_MENU_FOCUS,FG_MODE_SET,~0,FG_LINE_SOLID,selection,fg.displaybox);
  547.     selection[FG_X1]++;
  548.     selection[FG_Y1]++;
  549.     selection[FG_X2]--;
  550.     selection[FG_Y2]--;
  551.     fg_drawbox(COLOR_MENU_FOCUS,FG_MODE_SET,~0,FG_LINE_SOLID,selection,fg.displaybox);
  552.     fg_msm_showcursor();
  553.     fg_flush();
  554.     }
  555.  
  556.  
  557. /* clear a menu/popup selection */
  558. static void selection_unfocus(short x1,short y1,short x2,short y2)
  559.     {
  560.     fg_box_t selection;
  561.  
  562.     fg_msm_hidecursor();
  563.     fg_make_box(selection,x1,y1,x2,y2);
  564.     fg_drawbox(COLOR_MENU_BACKGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,selection,fg.displaybox);
  565.     selection[FG_X1]++;
  566.     selection[FG_Y1]++;
  567.     selection[FG_X2]--;
  568.     selection[FG_Y2]--;
  569.     fg_drawbox(COLOR_MENU_BACKGROUND,FG_MODE_SET,~0,FG_LINE_SOLID,selection,fg.displaybox);
  570.     fg_msm_showcursor();
  571.     fg_flush();
  572.     }
  573.  
  574.  
  575. /* ---------------- MENUS ---------------- */
  576.  
  577. /* close any open popups in menu and clear selected items, return 1 if OK or 0 if fail */
  578. static short menu_clear_any_items(MENU_ITEM *menu)
  579.     {
  580.     MENU_ITEM *m;
  581.  
  582.     for (m = menu ; m != NULL ; m = m->next)
  583.         if (m->status & MENU_SELECTED)
  584.             {
  585.             menu_clear_item(m);
  586.             if(m->popup != NULL)
  587.                 {
  588.                 if (!popup_close(m->popup))
  589.                     return 0;
  590.                 }
  591.             }
  592.     return 1;
  593.     }
  594.  
  595.  
  596. /* highlight a menu item and mark status as selected */
  597. static void menu_select_item(MENU_ITEM *menu)
  598.     {
  599.     selection_focus(menu->screen[FG_X1]+gui_char_width,menu->screen[FG_Y1]+1,menu->screen[FG_X2]-gui_char_width,menu->screen[FG_Y2]-1);
  600.     menu->status |= MENU_SELECTED;
  601.     }
  602.  
  603.  
  604. /* clear a menu item's highlights and status flag */
  605. static void menu_clear_item(MENU_ITEM *menu)
  606.     {
  607.     selection_unfocus(menu->screen[FG_X1]+gui_char_width,menu->screen[FG_Y1]+1,menu->screen[FG_X2]-gui_char_width,menu->screen[FG_Y2]-1);
  608.     menu->status &= ~MENU_SELECTED;
  609.     }
  610.  
  611.  
  612. /* move to previous menu item */
  613. static void menu_select_previous_item(MENU_ITEM *first,MENU_ITEM *current)
  614.     {
  615.     MENU_ITEM *m,*mark;
  616.  
  617.     mark = current;
  618.     do
  619.         {
  620.         for (m = first ; m->next != current && m->next != NULL ; m = m->next)
  621.             ;
  622.         current = m;
  623.         if (current == mark)
  624.             break;
  625.         } while ((current->status & MENU_ACTIVE) == 0);
  626.     if (current->popup != NULL)
  627.         popup_from_menu(first,current);
  628.     else
  629.         {
  630.         menu_clear_any_items(first);
  631.         menu_select_item(current);
  632.         }
  633.     }
  634.  
  635.  
  636. /* move to next menu item */
  637. static void menu_select_next_item(MENU_ITEM *first,MENU_ITEM *current)
  638.     {
  639.     MENU_ITEM *mark;
  640.  
  641.     mark = current;
  642.     do
  643.         {
  644.         if (current->next == NULL)
  645.             current = first;
  646.         else
  647.             current = current->next;
  648.         if (current == mark)
  649.             break;
  650.         } while ((current->status & MENU_ACTIVE) == 0);
  651.     if (current->popup != NULL)
  652.         popup_from_menu(first,current);
  653.     else
  654.         {
  655.         menu_clear_any_items(first);
  656.         menu_select_item(current);
  657.         }
  658.     }
  659.  
  660.  
  661. /* draw menu text str at coordinates x,y using ACTIVE,GRAY HIDDEN status, return new x value */
  662. static short menu_draw_text(short x,short y,char *str,unsigned short status)
  663.     {
  664.     short i,color;
  665.  
  666.     for (i = 0 ; str[i] != 0 ; i++)
  667.         {
  668.         if (status & MENU_GRAY)
  669.             color = COLOR_MENU_GRAY;
  670.         else if (status & MENU_HIDDEN)
  671.             color = COLOR_MENU_BACKGROUND;
  672.         else
  673.             color = COLOR_MENU_FOREGROUND;
  674.         if (str[i] == '&')
  675.             {
  676.             i++;
  677.             if (str[i] == 0)
  678.                 i--;
  679.             if (str[i] != '&' && color == COLOR_MENU_FOREGROUND)
  680.                 color = COLOR_MENU_HIGHLIGHT;
  681.             }
  682.         fg_putc(color,FG_MODE_SET,~0,FG_ROT0,x,y,str[i],fg.displaybox);
  683.         x += gui_char_width;
  684.         }
  685.     return x;
  686.     }
  687.  
  688.  
  689. /* closes any popups left open when a click occurs somewhere */
  690. static void menu_default_message_handler(MESSAGE *message)
  691.     {
  692.     short key;
  693.  
  694.     if (menu_item_is_selected != NULL)              /* close any open menu items */
  695.         {
  696.         menu_clear_any_items(menu_item_is_selected);
  697.         message->id = M_NONE;
  698.         }
  699.     else if (message->id == M_KEY)
  700.         {
  701.         key = message->data.short_data.x;
  702.         if (key == TAB || key == SHIFTTAB || key == DOWNARROW || key == UPARROW || key == RIGHTARROW || key == LEFTARROW || key == RETURN)
  703.             {                       /* nothing open, so open first item */
  704.             if (menu_last_seen->popup != NULL)
  705.                 popup_from_menu(menu_last_seen,menu_last_seen);
  706.             else
  707.                 {
  708.                 menu_clear_any_items(menu_last_seen);
  709.                 menu_select_item(menu_last_seen);
  710.                 }
  711.             message->id = M_NONE;
  712.             }
  713.         }
  714.     }
  715.  
  716.  
  717. /* return a pointer to the currently selected menu item or NULL if none */
  718. static MENU_ITEM *menu_current(MENU_ITEM *first)
  719.     {
  720.     MENU_ITEM *m;
  721.  
  722.     for (m = first ; m != NULL ; m = m->next)
  723.         if (m->status & MENU_SELECTED)
  724.             return m;
  725.     return NULL;
  726.     }
  727.  
  728.  
  729. /* redraw a modified menu item */
  730. static void menu_modify_draw(MENU_ITEM *menu)
  731.     {
  732.     short text_y;
  733.     fg_box_t text_area;
  734.  
  735.     fg_msm_hidecursor();
  736.     text_y = 3;
  737.     if (gui_char_height + gui_char_height/2 > gui_char_height + 6)
  738.         text_y = gui_char_height/4;
  739.     fg_box_cpy(text_area,menu->screen);
  740.     text_area[FG_X1] += 2*gui_char_width;
  741.     text_area[FG_Y1] += text_y;
  742.     text_area[FG_X2] -= 2*gui_char_width;
  743.     text_area[FG_Y2] = text_area[FG_Y1] + gui_char_height - 1;
  744.     fg_fillbox(COLOR_MENU_BACKGROUND,FG_MODE_SET,~0,text_area);
  745.     menu_draw_text(text_area[FG_X1],text_area[FG_Y1],menu->name,menu->status);
  746.     fg_msm_showcursor();
  747.     fg_flush();
  748.     }
  749.  
  750.  
  751. /* ---------------- POPUPS ---------------- */
  752.  
  753. /* close any open popups and open this popup */
  754. static short popup_from_menu(MENU_ITEM *first,MENU_ITEM *active)
  755.     {
  756.     short x,dx;
  757.     POPUP_ITEM *p;
  758.  
  759.     if (active->popup == NULL || active->number_of_popup_items <= 0)
  760.         return 0;
  761.     if (!menu_clear_any_items(first))
  762.         return 0;
  763.     for (p = active->popup, dx = 0 ; p != NULL ; p = p->next)   /* find width of popup */
  764.         {
  765.         x = (strlen(p->name)+2) * gui_char_width;
  766.         if (x > dx)
  767.             dx = x;
  768.         }
  769.     x = active->screen[FG_X1]+gui_char_width;
  770.     if (x + dx > fg.displaybox[FG_X2])      /* fit within menubar */
  771.         x = fg.displaybox[FG_X2] - dx;
  772.     if (x < fg.displaybox[FG_X1])
  773.         x = fg.displaybox[FG_X1];
  774.     if (!popup_open(active->popup,active->number_of_popup_items,x,active->screen[FG_Y1]))
  775.         return 0;
  776.     menu_select_item(active);
  777.     if (active->popup->status & MENU_ACTIVE)
  778.         popup_select_item(active->popup);
  779.     else
  780.         popup_select_next_item(active->popup,active->popup);
  781.     return 1;
  782.     }
  783.  
  784.  
  785. /* highlight a popup item and mark status as selected */
  786. static void popup_select_item(POPUP_ITEM *popup)
  787.     {
  788.     selection_focus(popup->screen[FG_X1]+1,popup->screen[FG_Y1]+1,popup->screen[FG_X2]-1,popup->screen[FG_Y2]-1);
  789.     popup->status |= MENU_SELECTED;
  790.     }
  791.  
  792.  
  793. /* clear all selected popup items */
  794. static void popup_clear_any_item(POPUP_ITEM *popup)
  795.     {
  796.     POPUP_ITEM *p;
  797.  
  798.     for (p = popup ; p != NULL ; p = p->next)
  799.         if (p->status & MENU_SELECTED)
  800.             popup_clear_item(p);
  801.     }
  802.  
  803.  
  804. /* clear a popup item's highlights and status flag */
  805. static void popup_clear_item(POPUP_ITEM *popup)
  806.     {
  807.     selection_unfocus(popup->screen[FG_X1]+1,popup->screen[FG_Y1]+1,popup->screen[FG_X2]-1,popup->screen[FG_Y2]-1);
  808.     popup->status &= ~MENU_SELECTED;
  809.     }
  810.  
  811.  
  812. /* move to previous popup item */
  813. static void popup_select_previous_item(POPUP_ITEM *first,POPUP_ITEM *current)
  814.     {
  815.     POPUP_ITEM *p,*mark;
  816.  
  817.     mark = current;
  818.     do
  819.         {
  820.         for (p = first ; p->next != current && p->next != NULL ; p = p->next)
  821.             ;
  822.         current = p;
  823.         if (current == mark)
  824.             break;
  825.         } while ((current->status & MENU_ACTIVE) == 0);
  826.     popup_clear_item(mark);
  827.     popup_select_item(current);
  828.     }
  829.  
  830.  
  831. /* move to next popup item */
  832. static void popup_select_next_item(POPUP_ITEM *first,POPUP_ITEM *current)
  833.     {
  834.     POPUP_ITEM *mark;
  835.  
  836.     mark = current;
  837.     do
  838.         {
  839.         if (current->next == NULL)
  840.             current = first;
  841.         else
  842.             current = current->next;
  843.         if (current == mark)
  844.             break;
  845.         } while ((current->status & MENU_ACTIVE) == 0);
  846.     popup_clear_item(mark);
  847.     popup_select_item(current);
  848.     }
  849.  
  850.  
  851. /* return a pointer to the currently selected popup item or NULL if none */
  852. static POPUP_ITEM *popup_current(POPUP_ITEM *first)
  853.     {
  854.     POPUP_ITEM *p;
  855.  
  856.     for (p = first ; p != NULL ; p = p->next)
  857.         if (p->status & MENU_SELECTED)
  858.             return p;
  859.     return NULL;
  860.     }
  861.  
  862.  
  863. /* redraw a modified popup item */
  864. static void popup_modify_draw(POPUP_ITEM *popup)
  865.     {
  866.     fg_box_t text_area;
  867.  
  868.     fg_msm_hidecursor();
  869.     fg_box_cpy(text_area,popup->screen);
  870.     text_area[FG_X1] += gui_char_width;
  871.     text_area[FG_Y1] += 3;
  872.     text_area[FG_X2] -= gui_char_width;
  873.     text_area[FG_Y2] = text_area[FG_Y1] + gui_char_height - 1;
  874.     fg_fillbox(COLOR_MENU_BACKGROUND,FG_MODE_SET,~0,text_area);
  875.     menu_draw_text(text_area[FG_X1],text_area[FG_Y1],popup->name,popup->status);
  876.     fg_msm_showcursor();
  877.     fg_flush();
  878.     }
  879.