home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / ncurses-1.9.9e-src.tgz / tar.out / fsf / ncurses / menu / m_driver.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  15KB  |  510 lines

  1.  
  2. /***************************************************************************
  3. *                            COPYRIGHT NOTICE                              *
  4. ****************************************************************************
  5. *                ncurses is copyright (C) 1992-1995                        *
  6. *                          Zeyd M. Ben-Halim                               *
  7. *                          zmbenhal@netcom.com                             *
  8. *                          Eric S. Raymond                                 *
  9. *                          esr@snark.thyrsus.com                           *
  10. *                                                                          *
  11. *        Permission is hereby granted to reproduce and distribute ncurses  *
  12. *        by any means and for any fee, whether alone or as part of a       *
  13. *        larger distribution, in source or in binary form, PROVIDED        *
  14. *        this notice is included with any such distribution, and is not    *
  15. *        removed from any of its header files. Mention of ncurses in any   *
  16. *        applications linked with it is highly appreciated.                *
  17. *                                                                          *
  18. *        ncurses comes AS IS with no warranty, implied or expressed.       *
  19. *                                                                          *
  20. ***************************************************************************/
  21.  
  22. /***************************************************************************
  23. * Module menu_driver and menu_pattern                                      *
  24. * Central dispatching routine and pattern matching handling                *
  25. ***************************************************************************/
  26.  
  27. #include "menu.priv.h"
  28.  
  29. /* Macros */
  30.  
  31. /* Remove the last character from the match pattern buffer */
  32. #define Remove_Character_From_Pattern(menu) \
  33.   (menu)->pattern[--((menu)->pindex)] = '\0'
  34.  
  35. /* Add a new character to the match pattern buffer */
  36. #define Add_Character_To_Pattern(menu,ch) \
  37.   { (menu)->pattern[((menu)->pindex)++] = (ch);\
  38.     (menu)->pattern[(menu)->pindex] = '\0'; }
  39.  
  40. /*---------------------------------------------------------------------------
  41. |   Facility      :  libnmenu  
  42. |   Function      :  static bool Is_Sub_String( 
  43. |                           bool IgnoreCaseFlag,
  44. |                           const char *part,
  45. |                           const char *string)
  46. |   
  47. |   Description   :  Checks whether or not part is a substring of string.
  48. |
  49. |   Return Values :  TRUE   - if it is a substring
  50. |                    FALSE  - if it is not a substring
  51. +--------------------------------------------------------------------------*/
  52. static bool Is_Sub_String(
  53.               bool  IgnoreCaseFlag,
  54.               const char *part,
  55.               const char *string
  56.              )
  57. {
  58.   assert( part && string );
  59.   if ( IgnoreCaseFlag )
  60.     {
  61.       while(*string && *part)
  62.     {
  63.       if (toupper(*string++)!=toupper(*part)) break;
  64.       part++;
  65.     }
  66.     }
  67.   else
  68.     {
  69.       while( *string && *part )
  70.     if (*part != *string++) break;
  71.       part++;
  72.     }
  73.   return ( (*part) ? FALSE : TRUE );
  74. }
  75.  
  76. /*---------------------------------------------------------------------------
  77. |   Facility      :  libnmenu  
  78. |   Function      :  static int Match_Next_Character_In_Item_Name(
  79. |                           MENU *menu,
  80. |                           int  ch,
  81. |                           ITEM **item)
  82. |   
  83. |   Description   :  This internal routine is called for a menu positioned
  84. |                    at an item with three different classes of characters:
  85. |                       - a printable character; the character is added to
  86. |                         the current pattern and the next item matching
  87. |                         this pattern is searched.
  88. |                       - NUL; the pattern stays as it is and the next item
  89. |                         matching the pattern is searched
  90. |                       - BS; the pattern stays as it is and the previous
  91. |                         item matching the pattern is searched
  92. |
  93. |                       The item parameter contains on call a pointer to
  94. |                       the item where the search starts. On return - if
  95. |                       a match was found - it contains a pointer to the
  96. |                       matching item.
  97. |  
  98. |   Return Values :  E_OK        - an item matching the pattern was found
  99. |                    E_NO_MATCH  - nothing found
  100. +--------------------------------------------------------------------------*/
  101. static int Match_Next_Character_In_Item_Name(MENU *menu, int ch, ITEM **item)
  102. {
  103.   bool found = FALSE, passed = FALSE;
  104.   int  idx, last;
  105.   
  106.   assert( menu && item && *item);
  107.   idx = (*item)->index;
  108.   
  109.   if (ch && ch!=BS)
  110.     {
  111.       /* if we become to long, we need no further checking : there can't be
  112.      a match ! */
  113.       if ((menu->pindex+1) > menu->namelen) 
  114.     RETURN(E_NO_MATCH);
  115.       
  116.       Add_Character_To_Pattern(menu,ch);
  117.       /* we artificially position one item back, because in the do...while
  118.      loop we start with the next item. This means, that with a new
  119.      pattern search we always start the scan with the actual item. If
  120.      we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the
  121.      one after or before the actual item. */
  122.       if (--idx < 0) 
  123.     idx = menu->nitems-1;
  124.     }
  125.   
  126.   last = idx;            /* this closes the cycle */
  127.   
  128.   do{
  129.     if (ch==BS)
  130.       {            /* we have to go backward */
  131.     if (--idx < 0) 
  132.       idx = menu->nitems-1;
  133.       }
  134.     else
  135.       {            /* otherwise we always go forward */
  136.     if (++idx >= menu->nitems) 
  137.       idx = 0;
  138.       }
  139.     if (Is_Sub_String((menu->opt & O_IGNORECASE) != 0,
  140.               menu->pattern,
  141.               menu->items[idx]->name.str)
  142.     )
  143.       found = TRUE;
  144.     else
  145.       passed = TRUE;    
  146.   } while (!found && (idx != last));
  147.   
  148.   if (found)
  149.     {
  150.       if (!((idx==(*item)->index) && passed))
  151.     {
  152.       *item = menu->items[idx];
  153.       RETURN(E_OK);
  154.     }
  155.       /* This point is reached, if we fully cycled through the item list
  156.      and the only match we found is the starting item. With a NEXT_PATTERN
  157.      or PREV_PATTERN scan this means, that there was no additional match.
  158.      If we searched with an expanded new pattern, we should never reach
  159.      this point, because if the expanded pattern matches also the actual
  160.      item we will find it in the first attempt (passed==FALSE) and we
  161.      will never cycle through the whole item array.   
  162.      */
  163.       assert( ch==0 || ch==BS );
  164.     }
  165.   else
  166.     {
  167.       if (ch && ch!=BS && menu->pindex>0)
  168.     {
  169.       /* if we had no match with a new pattern, we have to restore it */
  170.       Remove_Character_From_Pattern(menu);
  171.     }
  172.     }        
  173.   RETURN(E_NO_MATCH);
  174. }    
  175.  
  176. /*---------------------------------------------------------------------------
  177. |   Facility      :  libnmenu  
  178. |   Function      :  char *menu_pattern(const MENU *menu)
  179. |   
  180. |   Description   :  Return the value of the pattern buffer.
  181. |
  182. |   Return Values :  NULL          - if there is no pattern buffer allocated
  183. |                    EmptyString   - if there is a pattern buffer but no
  184. |                                    pattern is stored
  185. |                    PatternString - as expected
  186. +--------------------------------------------------------------------------*/
  187. char *menu_pattern(const MENU * menu)
  188. {
  189.   return (menu ? (menu->pattern ? menu->pattern : "") : (char *)0);
  190. }
  191.  
  192. /*---------------------------------------------------------------------------
  193. |   Facility      :  libnmenu  
  194. |   Function      :  int set_menu_pattern(MENU *menu, const char *p)
  195. |   
  196. |   Description   :  Set the match pattern for a menu and position to the
  197. |                    first item that matches.
  198. |
  199. |   Return Values :  E_OK              - success
  200. |                    E_BAD_ARGUMENT    - invalid menu or pattern pointer
  201. |                    E_NOT_CONNECTED   - no items connected to menu
  202. |                    E_BAD_STATE       - menu in user hook routine
  203. |                    E_NO_MATCH        - no item matches pattern
  204. +--------------------------------------------------------------------------*/
  205. int set_menu_pattern(MENU *menu, const char *p)
  206. {
  207.   ITEM *matchitem;
  208.   int   matchpos;
  209.   
  210.   if (!menu || !p)    
  211.     RETURN(E_BAD_ARGUMENT);
  212.   
  213.   if (!(menu->items))
  214.     RETURN(E_NOT_CONNECTED);
  215.   
  216.   if ( menu->status & _IN_DRIVER )
  217.     RETURN(E_BAD_STATE);
  218.   
  219.   Reset_Pattern(menu);
  220.   
  221.   if (!(*p))
  222.     {
  223.       pos_menu_cursor(menu);
  224.       RETURN(E_OK);
  225.     }
  226.   
  227.   if (menu->status & _LINK_NEEDED) 
  228.     _nc_Link_Items(menu);
  229.   
  230.   matchpos  = menu->toprow;
  231.   matchitem = menu->curitem;
  232.   assert(matchitem);
  233.   
  234.   while(*p)
  235.     {
  236.       if ( !isprint(*p) || 
  237.       (Match_Next_Character_In_Item_Name(menu,*p,&matchitem) != E_OK) )
  238.     {
  239.       Reset_Pattern(menu);
  240.       pos_menu_cursor(menu);
  241.       RETURN(E_NO_MATCH);
  242.     }
  243.       p++;
  244.     }            
  245.   
  246.   /* This is reached if there was a match. So we position to the new item */
  247.   Adjust_Current_Item(menu,matchpos,matchitem);
  248.   RETURN(E_OK);
  249. }
  250.  
  251. /*---------------------------------------------------------------------------
  252. |   Facility      :  libnmenu  
  253. |   Function      :  int menu_driver(MENU *menu, int c)
  254. |   
  255. |   Description   :  Central dispatcher for the menu. Translates the logical
  256. |                    request 'c' into a menu action.
  257. |
  258. |   Return Values :  E_OK            - success
  259. |                    E_BAD_ARGUMENT  - invalid menu pointer
  260. |                    E_BAD_STATE     - menu is in user hook routine
  261. |                    E_NOT_POSTED    - menu is not posted
  262. +--------------------------------------------------------------------------*/
  263. int menu_driver(MENU * menu, int   c)
  264. {
  265. #define NAVIGATE(dir) \
  266.   if (!item->dir)\
  267.      result = E_REQUEST_DENIED;\
  268.   else\
  269.      item = item->dir
  270.  
  271.   int result = E_OK;
  272.   ITEM *item;
  273.   int my_top_row, rdiff;
  274.   
  275.   if (!menu)
  276.     RETURN(E_BAD_ARGUMENT);
  277.   
  278.   if ( menu->status & _IN_DRIVER )
  279.     RETURN(E_BAD_STATE);
  280.   if ( !( menu->status & _POSTED ) )
  281.     RETURN(E_NOT_POSTED);
  282.   
  283.   my_top_row = menu->toprow;
  284.   item    = menu->curitem;
  285.   assert(item);
  286.   
  287.   if ((c > KEY_MAX) && (c<=MAX_MENU_COMMAND))
  288.     {  
  289.       if (!((c==REQ_BACK_PATTERN)
  290.         || (c==REQ_NEXT_MATCH) || (c==REQ_PREV_MATCH)))
  291.     {
  292.       assert( menu->pattern );
  293.       Reset_Pattern(menu);
  294.     }
  295.       
  296.       switch(c)
  297.     {
  298.     case REQ_LEFT_ITEM:
  299.       /*=================*/  
  300.       NAVIGATE(left);
  301.       break;
  302.       
  303.     case REQ_RIGHT_ITEM:
  304.       /*==================*/  
  305.       NAVIGATE(right);
  306.       break;
  307.       
  308.     case REQ_UP_ITEM:
  309.       /*===============*/  
  310.       NAVIGATE(up);
  311.       break;
  312.       
  313.     case REQ_DOWN_ITEM:
  314.       /*=================*/  
  315.       NAVIGATE(down);
  316.       break;
  317.       
  318.     case REQ_SCR_ULINE:
  319.       /*=================*/  
  320.       if (my_top_row == 0)
  321.         result = E_REQUEST_DENIED;
  322.       else
  323.         {
  324.           --my_top_row;
  325.           item = item->up;
  326.         }  
  327.       break;
  328.       
  329.     case REQ_SCR_DLINE:
  330.       /*=================*/  
  331.       my_top_row++;
  332.       if ((menu->rows - menu->height)>0)
  333.         {
  334.           /* only if the menu has less items than rows, we can deny the
  335.          request. Otherwise the epilogue of this routine adjusts the
  336.          top row if necessary */
  337.           my_top_row--;
  338.           result = E_REQUEST_DENIED;
  339.         }
  340.       else
  341.         item = item->down;
  342.       break;
  343.       
  344.     case REQ_SCR_DPAGE:
  345.       /*=================*/  
  346.       rdiff = menu->rows - menu->height - my_top_row;
  347.       if (rdiff > menu->height) 
  348.         rdiff = menu->height;
  349.       if (rdiff==0)
  350.         result = E_REQUEST_DENIED;
  351.       else
  352.         {
  353.           my_top_row += rdiff;
  354.           while(rdiff-- > 0)
  355.         item = item->down;
  356.         }
  357.       break;
  358.       
  359.     case REQ_SCR_UPAGE:
  360.       /*=================*/  
  361.       rdiff = (menu->height < my_top_row) ?
  362.         menu->height : my_top_row;
  363.       if (rdiff==0)
  364.         result = E_REQUEST_DENIED;
  365.       else
  366.         {
  367.           my_top_row -= rdiff;
  368.           while(rdiff--)
  369.         item = item->up;
  370.         }
  371.       break;
  372.       
  373.     case REQ_FIRST_ITEM:
  374.       /*==================*/  
  375.       item = menu->items[0];
  376.       break;
  377.       
  378.     case REQ_LAST_ITEM:
  379.       /*=================*/  
  380.       item = menu->items[menu->nitems-1];
  381.       break;
  382.  
  383.     case REQ_NEXT_ITEM:
  384.       /*=================*/  
  385.       if ((item->index+1)>=menu->nitems)
  386.         {
  387.           if (menu->opt & O_NONCYCLIC)
  388.         result = E_REQUEST_DENIED;
  389.           else
  390.         item = menu->items[0];
  391.         }
  392.       else
  393.         item = menu->items[item->index + 1];
  394.       break;
  395.       
  396.     case REQ_PREV_ITEM:
  397.       /*=================*/  
  398.       if (item->index<=0)
  399.         {
  400.           if (menu->opt & O_NONCYCLIC)
  401.         result = E_REQUEST_DENIED;
  402.           else
  403.         item = menu->items[menu->nitems-1];
  404.         }
  405.       else
  406.         item = menu->items[item->index - 1];
  407.       break;
  408.       
  409.     case REQ_TOGGLE_ITEM:
  410.       /*===================*/  
  411.       if (menu->opt & O_ONEVALUE)
  412.         {
  413.           result = E_REQUEST_DENIED;
  414.         }
  415.       else
  416.         {
  417.           if (menu->curitem->opt & O_SELECTABLE)
  418.         {
  419.           menu->curitem->value = TRUE;
  420.           Move_And_Post_Item(menu,menu->curitem);
  421.           _nc_Show_Menu(menu);
  422.         }
  423.           else
  424.         result = E_NOT_SELECTABLE;
  425.         }
  426.       break;
  427.       
  428.     case REQ_CLEAR_PATTERN:
  429.       /*=====================*/  
  430.       /* already cleared in prologue */
  431.       break;
  432.       
  433.     case REQ_BACK_PATTERN:
  434.       /*====================*/  
  435.       if (menu->pindex>0)
  436.         {
  437.           assert(menu->pattern);
  438.           Remove_Character_From_Pattern(menu);
  439.           pos_menu_cursor( menu );
  440.         }
  441.       else
  442.         result = E_REQUEST_DENIED;
  443.       break;
  444.       
  445.     case REQ_NEXT_MATCH:
  446.       /*==================*/  
  447.       assert(menu->pattern);
  448.       if (menu->pattern[0])
  449.         result = Match_Next_Character_In_Item_Name(menu,0,&item);
  450.       else
  451.         {
  452.           if ((item->index+1)<menu->nitems)
  453.         item=menu->items[item->index+1];
  454.           else
  455.         {
  456.           if (menu->opt & O_NONCYCLIC)
  457.             result = E_REQUEST_DENIED;
  458.           else
  459.             item = menu->items[0];
  460.         }
  461.         }
  462.       break;    
  463.       
  464.     case REQ_PREV_MATCH:
  465.       /*==================*/  
  466.       assert(menu->pattern);
  467.       if (menu->pattern[0])
  468.         result = Match_Next_Character_In_Item_Name(menu,BS,&item);
  469.       else
  470.         {
  471.           if (item->index)
  472.         item = menu->items[item->index-1];
  473.           else
  474.         {
  475.           if (menu->opt & O_NONCYCLIC)
  476.             result = E_REQUEST_DENIED;
  477.           else
  478.             item = menu->items[menu->nitems-1];
  479.         }
  480.         }
  481.       break;
  482.       
  483.     default:
  484.       /*======*/  
  485.       result = E_UNKNOWN_COMMAND;
  486.       break;
  487.     }
  488.     }
  489.   else
  490.     {                /* not a command */
  491.       if ( !(c & ~((int)MAX_REGULAR_CHARACTER)) && isprint(c) )
  492.     result = Match_Next_Character_In_Item_Name( menu, c, &item );
  493.       else
  494.     result = E_UNKNOWN_COMMAND;
  495.     }
  496.   
  497.   /* Adjust the top row if it turns out that the current item unfortunately
  498.      doesn't appear in the menu window */
  499.   if ( item->y < my_top_row )
  500.     my_top_row = item->y;
  501.   else if ( item->y >= (my_top_row + menu->height) )
  502.     my_top_row = item->y - menu->height + 1;
  503.   
  504.   _nc_New_TopRow_and_CurrentItem( menu, my_top_row, item );
  505.   
  506.   RETURN(result);
  507. }
  508.  
  509. /* m_driver.c ends here */
  510.