home *** CD-ROM | disk | FTP | other *** search
/ No Fragments Archive 12: Textmags & Docs / nf_archive_12.iso / MAGS / SOURCES / ATARI_SRC.ZIP / atari source / FALCON / ACC / DRIVERS.ACC / POPMENU.C < prev    next >
Encoding:
C/C++ Source or Header  |  2001-02-10  |  68.7 KB  |  2,429 lines

  1. /* POPMENU.C
  2.  *==========================================================================
  3.  * DESCRIPTION: Generic Popup Routines ( New and Improved )
  4.  * VERSION 0.00  Started:    February 7, 1991
  5.  * VERSION 0.10            March    4, 1991
  6.  * version 1.00            April    24,1991
  7.  *                Recreated damaged files
  8.  *                Removed open workstation calls.
  9.  *                vhandle = graf_handle()
  10.  * December 4, 1992 REMOVED MAJOR AMOUNTS OF CODE.
  11.  *            This version is OLD OLD OLD compared to the
  12.  *            AES 4.0 version - Its DRASTICALLY Different.
  13.  *            For this app, only check mark, disable and submenus
  14.  *            are supported.
  15.  *
  16.  *
  17.  * INCLUDE: POPMENU.H
  18.  * Requires: POP_HEAD.H
  19.  *
  20.  */
  21.  
  22.  
  23.  
  24. /* INCLUDE FILES
  25.  *==========================================================================
  26.  */
  27. #include <sys\gemskel.h>
  28. #include <string.h >
  29. #include <tos.h>
  30. #include <stdlib.h>
  31. #include <stdio.h>
  32.  
  33. #include "pop_head.h"
  34. #include "device.h"
  35.  
  36.  
  37. /* EXTERNS
  38.  *==========================================================================
  39.  */
  40. extern int xres, yres;
  41.  
  42.  
  43. /* PROTOTYPES
  44.  *==========================================================================
  45.  */
  46. void    InitPopUpMenus( void );
  47. int    GetNewPopMenu( void );
  48. POP_PTR    GetMenuPtr( int MenuID );
  49.  
  50. BOOLEAN    InitCmdChar( POP_PTR PopPtr );
  51. void    BuildCmdChar( POP_PTR PopPtr );
  52. void    BuildKeyCodes( POP_PTR PopPtr, char *text, int num_entry );
  53. void    DeletePopUpMenu( int MenuID );
  54.  
  55. int     InsertPopUpMenu( char *text, int num_entries, int height );
  56. long    PopUpMenuSelect( int MenuID, int xpos, int ypos, int Item );
  57. BOOLEAN Pop_Blit( long *PopPtr, GRECT *xclip, int flag );
  58.  
  59.  
  60. BOOLEAN Build_Objects( POP_PTR curptr );
  61. void    SetRootNode( OBJECT *tree, int xpos, int ypos, int Width, int Num );
  62. void    SetTextNode( OBJECT *tree, int parent, POP_PTR PopPtr, int obj, int Ypos, int textnum, int Width );
  63. long    CalcTextBufferSize( POP_PTR PopPtr );
  64. void    BuildText( POP_PTR PopPtr );
  65. int    CountBlanksNeeded( char *dindex, char *dtemp, int width, int num );
  66.  
  67.  
  68. BOOLEAN    Pop_Arrow( POP_PTR PopPtr, int obj );
  69. void    UpArrowStatus( POP_PTR PopPtr );
  70. void    DownArrowStatus( POP_PTR PopPtr );
  71. void    SetArrowClickDelay( long delay );
  72.  
  73. /* KEYCODE CHECKING */
  74. long    CheckForKeyCodes( POP_PTR PopPtr, char *text, int num_entry );
  75. char    *CheckForCtrl( char *text );
  76. char    *CheckForAlt( char *text );
  77. char    *CheckForCapKey( char *text );
  78. char    *CheckForCmdKey( char *text );
  79. BOOLEAN CheckForDisable( char *text );
  80. BOOLEAN    CheckForMenuCheck( char *text );
  81. char     *CheckForSubMenu( char *text );
  82. char    *CheckForFuncKey( char *text );
  83. BOOLEAN    CheckForDuplicate( char *text );
  84.    
  85. /* APPEARANCE OF ITEMS */
  86. void    SetHeight( int MenuID, int Height );
  87. void    SetNumItems( int MenuID, int NumItems );
  88.  
  89. void    CheckItem( int MenuID, int item, BOOLEAN check );
  90. void    DisableItem( int MenuID, int item );
  91. void    EnableItem( int MenuID, int item );
  92.    
  93. void    SetItemCmd( int MenuID, int item, char cmd );
  94. char    GetItemCmd( int MenuID, int item );
  95.  
  96. void    SetSubMenuID( int MenuID, int item, int ID );
  97. int    GetSubMenuID( int MenuID, int item );
  98.  
  99. void    SetItemMark( int MenuID, int item, char key );
  100. char    GetItemMark( int MenuID, int item );
  101.  
  102. void    SetFuncMark( int MenuID, int item, int num );
  103. int     GetFuncMark( int MenuID, int item );
  104.  
  105. void    SetItem( int MenuID, int item, char *text );
  106. char    *GetItem( int MenuID, int item );
  107.  
  108. int    GetStartItem( int MenuID );
  109. void    SetStartItem( int MenuID, int item );
  110.  
  111.  
  112. /* SUBMENU HANDLING */
  113. POP_PTR    DoSubMenu( POP_PTR PopPtr, int obj );
  114. BOOLEAN    ShowSubMenu( int MenuID, int xpos, int ypos, GRECT *rect );
  115. void    HideSubMenu( POP_PTR PopPtr );
  116. long    EvntSubMenu( POP_PTR PopPtr );
  117. int    FindNum( POP_PTR PopPtr, int obj );
  118. long    MenuChoice( void );
  119.  
  120.  
  121. /* SUBMENU DELAY */
  122. long    GetTimeHz( void );
  123. void    SetSubMenuDelay( long ms );
  124. void    SetSubDragDelay( long ms );
  125.  
  126.  
  127. void    AdjustToScreen( POP_PTR PopPtr, int xpos, int ypos, GRECT *rect, BOOLEAN byte_flag, BOOLEAN Display_Flag );
  128. void    WaitForUpButton( void );
  129.  
  130.  
  131.  
  132. /* EXTERNALS
  133.  *==========================================================================
  134.  * These functions and variables are required and must be
  135.  * supplied externally.
  136.  */
  137.  
  138. int    BUT_OR( void );        /* Used to ensure left mouse button */
  139. extern  int   (*BADDR)( void );
  140. extern  int    BSTATE;
  141. extern  int    BREAL;
  142.  
  143.  
  144. /* DEFINES
  145.  *==========================================================================
  146.  */
  147. #define INIT_DISPLAY_DELAY  300L    /* Initial Submenu Display Delay   */ 
  148. #define INIT_DRAG_DELAY     3000L   /* Initial Submenu Drag Delay      */
  149. #define INIT_CLICK_DELAY    150L   /* Delay for STARTING arrow scroll */
  150.  
  151.  
  152.  
  153. #define STLOW    2
  154. #define STMED    3
  155.  
  156. #define MAX_MENUS    10       /* Maximum # of Menus allowed...*/
  157.                    /* DEFINED TO BE 10 MENUS FOR THIS APP*/
  158.  
  159.  
  160. #define UP_ARROW    0x01
  161. #define DOWN_ARROW    0x02
  162. #define RIGHT_ARROW    0x03
  163.  
  164.  
  165.  
  166. /* GLOBAL
  167.  *==========================================================================
  168.  */
  169. POP_NODE MenuList[ MAX_MENUS ];        /* Menu structure Storage */
  170. BOOLEAN  free_flag[ MAX_MENUS ];
  171. char CmdText[ 10 ];        /* Temp string storage...       */
  172. char TempString[ 255 ];
  173. int  xout[57];            /* used for vq_extnd() */
  174.  
  175.  
  176. long SUBMENU_DELAY;        /* Delay time for submenus to appear ( ms )    */
  177. long SUBDRAG_DELAY;        /* Delay time for submenus to go active( ms)   */
  178. long CLICK_DELAY;        /* Delay time for arrows to start scrolling(ms)*/
  179. long TimeInHz;            /* Current Time in 200 Hz */
  180.  
  181.                 /* Used by the MenuChoice() call         */
  182. int  CurObject;            /* Current Object clicked on. ( Global ) */
  183. int  CurMenu;            /* Current Menu clicked on. ( Global )   */
  184.  
  185.  
  186. /* FUNCTIONS
  187.  *==========================================================================
  188.  */
  189.  
  190.  
  191. /* InitPopMenus()
  192.  * ====================================================================
  193.  * Simply clears out the flags in the free_flags structure.
  194.  */
  195. void
  196. InitPopUpMenus( void )
  197. {
  198.    int i;
  199.    
  200.    for( i = 0; i < MAX_MENUS; i++ )
  201.       free_flag[ i ] = FALSE;
  202.    SetSubMenuDelay( INIT_DISPLAY_DELAY );
  203.    SetSubDragDelay( INIT_DRAG_DELAY );        
  204.    SetArrowClickDelay( INIT_CLICK_DELAY );
  205. }
  206.  
  207.  
  208.  
  209. /* InsertPopMenu()
  210.  * ====================================================================
  211.  * Returns: Pointer to the POPUP structure.
  212.  * IN:   
  213.  *       XHEIGHT ( in characters ) - actually, number of entries displayed
  214.  *     NUM_ENTRIES           - Total # of menu entries.
  215.  *     Pointer to text address...
  216.  * OUT: # >= 0    Valid Menu ID
  217.  *      # < 0     Error messages.
  218.  *                -1 = Memory Allocation Error.
  219.  *          -2 = No more Menu Slots available.
  220.  */
  221. int
  222. InsertPopUpMenu( char *text, int num_entries, int height )
  223. {
  224.     POP_PTR PopPtr;
  225.     int     MenuID;
  226.     
  227.     MenuID = GetNewPopMenu();
  228.     
  229.     /* No more menu slots available.*/
  230.     if( MenuID == -1 )
  231.        return( -2 );
  232.  
  233.     PopPtr = GetMenuPtr( MenuID );
  234.     PPREV( PopPtr )   = ( POP_PTR )NULL;
  235.        
  236.     PMENUID( PopPtr ) = MenuID;     
  237.     PXPOS( PopPtr )   = PYPOS( PopPtr ) = 0;
  238.     PNUM( PopPtr )    = num_entries;
  239.     POBJECT( PopPtr ) = ( OBJECT *)NULL;
  240.     
  241.     /* Take care of height of menu */ 
  242.     if( height > PNUM( PopPtr ) )
  243.         height = PNUM( PopPtr );
  244.     PHEIGHT( PopPtr )     = height;
  245.     PIX_HEIGHT( PopPtr )  = ( PHEIGHT( PopPtr ) * gl_hchar );
  246.     PTEXT( PopPtr )       = text;
  247.     
  248.     if( !InitCmdChar( PopPtr ))
  249.     {
  250.        /* If memory error, clear out the Menu Slot
  251.         * and return a -1.
  252.         */
  253.        free_flag[ MenuID ] = FALSE;       
  254.        MenuID = -1;
  255.     }
  256.     return( MenuID );
  257. }
  258.  
  259.  
  260.  
  261. /* GetNewPopMenu()
  262.  * ====================================================================
  263.  * Find an unused slot for a menu.
  264.  * RETURN( -1 ) if there are no more menu slots available.
  265.  */
  266. int
  267. GetNewPopMenu( void )
  268.     int i;
  269.     
  270.     for( i = 0; i < MAX_MENUS; i++ )
  271.     {
  272.     if( !free_flag[ i ] )
  273.     {
  274.        free_flag[i] = TRUE;
  275.        return( i );
  276.     }   
  277.     }
  278.     return( -1 );
  279. }
  280.  
  281.  
  282.  
  283.  
  284. /* GetMenuPtr()
  285.  * ====================================================================
  286.  * Returns a pointer to the Menu Structure if given a valid menuID
  287.  */
  288. POP_PTR
  289. GetMenuPtr( int MenuID )
  290. {
  291.     return( ( POP_PTR )&MenuList[ MenuID ] );
  292. }
  293.  
  294.  
  295.  
  296. /* InitCmdChar()
  297.  * ====================================================================
  298.  * Initializes the CmdChar memory for a menu structure.
  299.  * The memory is MALLOCed! So the application MUST call
  300.  * DeletePopMenu() for each menu created. OR ELSE...!
  301.  * Need to check for malloc failures.
  302.  * RETURN: FALSE = ERROR!
  303.  *       TRUE  = AOK!
  304.  * It will be up to the program to inform the user that
  305.  * no menu was created and initialized.
  306.  */
  307. BOOLEAN
  308. InitCmdChar( POP_PTR PopPtr )
  309. {
  310.     int i;
  311.     
  312.     PCmdChar( PopPtr ) = ( CmdChar *)malloc( (long)( (long)PNUM( PopPtr ) + 10L ) * (long)sizeof( CmdChar ));
  313.     /* We go up to + 2 because we have ...
  314.      * PNUM() = UP ARROW TEXT
  315.      * PNUM+1 = DOWN ARROW TEXT
  316.      * PNUM+2 = BLANK TEXT
  317.      */
  318.     if( PCmdChar( PopPtr ) )
  319.     { 
  320.       for( i = 0; i <= ( PNUM( PopPtr ) + 2 ); i++ )
  321.       {
  322.          CmdState( PopPtr->CharPtr[ i ] ) = NORMAL;
  323.          CmdSubMenu( PopPtr->CharPtr[ i ] ) = -1;
  324.          CmdFlag( PopPtr->CharPtr[i] ) = '\0';
  325.       }
  326.       
  327.       BuildCmdChar( PopPtr );
  328.       return( TRUE );
  329.     }
  330.     else
  331.       return( FALSE );
  332. }
  333.  
  334.  
  335.  
  336.  
  337. /* BuildCmdChar()
  338.  * ====================================================================
  339.  * Calculate the text buffer size that we'll have to malloc, based upon
  340.  * the largest text string and the number of entries.
  341.  */
  342. void
  343. BuildCmdChar( POP_PTR PopPtr )
  344. {
  345.    char *txtptr;
  346.    long length;
  347.    int  count;
  348.    
  349.    txtptr = PTEXT( PopPtr );
  350.    if( txtptr )
  351.    {
  352.        for( count = 0; count < PNUM( PopPtr ); count++ )
  353.        {    
  354.          length = strlen( txtptr );
  355.          BuildKeyCodes( PopPtr, txtptr, count );
  356.          txtptr += ( length + 1 );
  357.        }
  358.    }
  359. }
  360.  
  361.  
  362.  
  363.  
  364.  
  365. /* BuildKeyCodes()
  366.  * =====================================================================
  367.  * Checks the text string for various keycodes and subtracts
  368.  * an appropriate amount from the length if any are found.
  369.  * For many of these, there should only be one of them.
  370.  * We then ADD back in the amount of ACTUAL text usage this item
  371.  * would take. IE: a Function Key can be F10 so 3 characters...
  372.  * or a CmdKey can be the letters: 'DELETE' 6 characters added.
  373.  * This will also fill up the CMDCHAR structures.
  374.  */
  375. void
  376. BuildKeyCodes( POP_PTR PopPtr, char *text, int num_entry )
  377. {
  378.     char *txtptr;
  379.     
  380.     /* Check for the '@' character which will tell us to
  381.      * disable the current menu item that we are working on.
  382.      */
  383.     
  384.     if( CheckForDisable( text ) )
  385.       DisableItem( PMENUID( PopPtr ), num_entry );
  386.     else   
  387.       EnableItem( PMENUID( PopPtr ), num_entry );
  388.  
  389.     /* Check for CheckMark  ( ! ) */
  390.     CheckItem( PMENUID( PopPtr ), num_entry, CheckForMenuCheck( text ) );
  391.  
  392.     if( ( txtptr = CheckForSubMenu( text ) ) != NULL )
  393.     {
  394.     CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '~';
  395.     txtptr++;
  396.     CmdSubMenu( PopPtr->CharPtr[ num_entry ] ) = atoi( txtptr );
  397.     } 
  398. }
  399.  
  400.  
  401.  
  402.  
  403. /* DeletePopUpMenu()
  404.  * ====================================================================
  405.  */
  406. void
  407. DeletePopUpMenu( int MenuID )
  408. {
  409.    POP_PTR PopPtr;
  410.    
  411.    if( free_flag[ MenuID ] )
  412.    {
  413.      free_flag[ MenuID ] = FALSE;
  414.      PopPtr = GetMenuPtr( MenuID );
  415.      
  416.      if( PCmdChar( PopPtr ) )
  417.        free( PCmdChar( PopPtr ) );
  418.        
  419.      PCmdChar( PopPtr ) = ( CmdChar *)NULL;
  420.    }  
  421. }
  422.  
  423.  
  424.  
  425.  
  426. /* PopUpMenuSelect()
  427.  * ====================================================================
  428.  * Displays the actual popup menu.
  429.  * Let's the user select an object.
  430.  * IN:   POP_PTR PopPtr;
  431.  *       int     xpos;
  432.  *       int     ypos;
  433.  *       int     Start_Obj;  Index to show selected else start with 0
  434.  *       BOOLEAN Arrow_Flag; If TRUE, display arrows if height < num_items.
  435.  *                    FALSE, don't display arrows if height < num_items.
  436.  * returns:  long where....
  437.  *        HIGH WORD    LOW WORD
  438.  *         MenuID          Item Selected.
  439.  *           -1L if NONE selected.
  440.  *         If the MenuID is valid, but the item is -1, then
  441.  *         the user clicked on a disabled menu item.
  442.  *         -2L if Memory Allocation Error
  443.  *         -3L if workstation error.
  444.  */
  445. long
  446. PopUpMenuSelect( int MenuID, int xpos, int ypos, int Item )
  447. {
  448.     
  449.     long    obj;
  450.     POP_PTR PopPtr;
  451.     GRECT   rect;
  452.     MRETS   mk;
  453.     int     max_height;
  454.         
  455.     WaitForUpButton();
  456.     wind_update( BEG_MCTRL );
  457.  
  458.     if( !open_vwork() )
  459.       return( -2L );
  460.  
  461.     vq_extnd( vhandle, 0, xout );
  462.     xres = xout[0];
  463.     yres = xout[1];    
  464.  
  465.     vex_butv( vhandle, BUT_OR, &BADDR );    
  466.     
  467.     PopPtr = GetMenuPtr( MenuID );
  468.     PopUpItem( PopPtr ) = Item;
  469.  
  470.     PXPOS( PopPtr )   = xpos;
  471.     PYPOS( PopPtr )   = ypos;
  472.     PPREV( PopPtr )   = ( POP_PTR )NULL;
  473.  
  474.     /* Take care of height of menu */ 
  475.     if( PHEIGHT( PopPtr ) > PNUM( PopPtr ) )
  476.         PHEIGHT( PopPtr ) = PNUM( PopPtr );
  477.  
  478.     /* Limit Height to Height of screen - ( 2 * gl_hchars ) */
  479.     max_height = yres - ( 2 * gl_hchar );
  480.     while( ( PHEIGHT( PopPtr ) * gl_hchar ) > max_height )
  481.        PHEIGHT( PopPtr ) -= 1;
  482.  
  483.     PIX_HEIGHT( PopPtr )  = ( PHEIGHT( PopPtr ) * gl_hchar );
  484.  
  485.        
  486.     /* Take care of the width of the menu here...*/
  487.     PTEXTBUFFSIZE( PopPtr ) = CalcTextBufferSize( PopPtr );
  488.     PIX_WIDTH( PopPtr )     = ( PWIDTH( PopPtr ) * gl_wchar );
  489.  
  490.     AdjustToScreen( PopPtr, xpos, ypos, &rect, FALSE, TRUE );
  491.     if( Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 0 ) )
  492.     {
  493.        if( Build_Objects( PopPtr ) )
  494.        {
  495.          obj = EvntSubMenu( PopPtr );
  496.          if( obj == -2L ) /* returning from a submenu - error */
  497.              obj = -1L;    /* then set it to click on nothing */
  498.          Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 1 );
  499.        }
  500.        else    /* Memory Allocation Error in Space Capsule */
  501.          obj = -2L;  
  502.  
  503.        if( POBJECT( PopPtr ) )
  504.            free( POBJECT( PopPtr ) );
  505.         
  506.        if( PTEXTBUFF( PopPtr ) )    
  507.            free( PTEXTBUFF( PopPtr ) );
  508.         
  509.     }
  510.     else    /* ERROR: Memory Allocation Error */
  511.       obj = -2L;
  512.  
  513.     POBJECT( PopPtr )   = ( OBJECT *)NULL;
  514.     PTEXTBUFF( PopPtr ) = ( char *)NULL;
  515.     
  516.     Evnt_button( 1, 1, 0, &mk );
  517.  
  518.     vex_butv( vhandle, BADDR, &BADDR );
  519.     close_vwork();
  520.     
  521.     wind_update( END_MCTRL );
  522.  
  523.     return( obj );
  524. }
  525.  
  526.  
  527.  
  528.  
  529. /* Build_Objects()
  530.  * ====================================================================
  531.  * RETURN: TRUE - AOK
  532.  *         FALSE- Memory Allocation Error
  533.  */
  534. BOOLEAN
  535. Build_Objects( POP_PTR PopPtr )
  536. {
  537.    int     num_objects;
  538.    GRECT   rect;
  539.    int     text_index;        /* Index into the text data 0 based */
  540.    int     count;
  541.    int     ypos;        /* Y position of object ( in characters*/
  542.    int     temp;
  543.           
  544.    OBJECT *tree;
  545.    int    obj;
  546.  
  547.    /* Calculate the number of objects needed...
  548.     * ROOT + # of STRINGS + ( safety buffer )
  549.     */
  550.    num_objects = 1 + PHEIGHT( PopPtr ) + 10; /* + 10 for safety buffer */
  551.  
  552.    /* get the memory for this number of objects */   
  553.    POBJECT( PopPtr ) = ( OBJECT * )malloc( (long)(sizeof( OBJECT ) * (long)num_objects )); 
  554.     
  555.    /* Malloc the text memory and then build them. */
  556.    /* Need malloc checking of course...           */
  557.    PTEXTBUFF( PopPtr) = malloc( PTEXTBUFFSIZE( PopPtr ) );
  558.    
  559.    /* Memory Allocation Error */
  560.    if( !POBJECT( PopPtr ) || !PTEXTBUFF( PopPtr ) )
  561.        return( FALSE );
  562.  
  563.    BuildText( PopPtr );
  564.  
  565.        
  566.    /* setup the root */
  567.    tree = POBJECT( PopPtr );
  568.    SetRootNode( tree, PXPOS( PopPtr ), PYPOS( PopPtr ),
  569.                 PWIDTH( PopPtr ), PHEIGHT( PopPtr ) );
  570.    
  571.    ypos = text_index = 0;
  572.    
  573.    obj = 1;            /* Start Creating from object 1 */
  574.    PFIRST( PopPtr ) = 1;    /* first G_STRING starts at 3   */
  575.  
  576.    POFFSET( PopPtr )   = ( PopUpItem( PopPtr ) + ( PNUM( PopPtr ) > PHEIGHT( PopPtr ) )) / PHEIGHT( PopPtr );
  577.  
  578.    if( POFFSET( PopPtr ) )
  579.    {
  580.        POFFSET( PopPtr ) = PopUpItem( PopPtr );
  581.        temp = PNUM( PopPtr ) - PHEIGHT( PopPtr ) + 1;
  582.  
  583.        if( POFFSET( PopPtr ) >= temp )
  584.            POFFSET( PopPtr ) = temp;
  585.    }      
  586.    text_index = POFFSET( PopPtr );
  587.    
  588.    for( count = 0; count < PHEIGHT( PopPtr ); count++ )
  589.    {
  590.       if( count == 0 )
  591.       {
  592.        if( POFFSET( PopPtr ) )
  593.              SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, PNUM( PopPtr ), PWIDTH( PopPtr ) );
  594.            else  
  595.            {
  596.              if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED )
  597.                  CheckObj( obj );
  598.              SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) );
  599.            }  
  600.       }  
  601.       else
  602.       {
  603.         if( count >=  ( PHEIGHT( PopPtr ) - 1 ) )
  604.         {
  605.            if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr ) ) < PNUM( PopPtr ) )
  606.               SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, PNUM( PopPtr ) + 1, PWIDTH( PopPtr ) );
  607.            else
  608.            {
  609.               if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED )
  610.                  CheckObj( obj );
  611.  
  612.               SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) );
  613.            }   
  614.         }
  615.         else      
  616.         {
  617.            if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED )
  618.                CheckObj( obj );
  619.  
  620.            SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) );
  621.         } 
  622.       }
  623.    }   
  624.    PLAST( PopPtr ) = obj - 1;
  625.    ObFlags( obj - 1 ) = LASTOB;
  626.  
  627.    rect = ObRect( ROOT );
  628.    rect.g_x -= 2;
  629.    rect.g_y -= 2;
  630.    rect.g_w += 5;
  631.    rect.g_h += 5;
  632.    rc_intersect( &desk, &rect );        /* clip to desktop */
  633.    Objc_draw( tree, ROOT, MAX_DEPTH, &rect );
  634.    return( TRUE );
  635. }
  636.  
  637.  
  638.  
  639. /* SetRootNode()
  640.  * ====================================================================
  641.  */
  642. void
  643. SetRootNode( OBJECT *tree, int xpos, int ypos, int Width, int Num )
  644. {
  645.     ObNext( ROOT )  = -1;
  646.     ObHead( ROOT )  = -1;
  647.     ObTail( ROOT )  = -1;
  648.     ObType( ROOT )  = G_BOX;
  649.     ObFlags( ROOT ) = NONE;
  650.     ObState( ROOT ) = SHADOWED;
  651.     ObIndex( ROOT ) = 0xFF1100L;
  652.  
  653.     /* fix up for screen...*/
  654.     ObX( ROOT ) = xpos;
  655.     ObY( ROOT ) = ypos;
  656.     ObW( ROOT ) = Width * gl_wchar;
  657.     ObH( ROOT ) = Num * gl_hchar;
  658. }
  659.  
  660.  
  661.  
  662. /* SetTextNode()
  663.  * ====================================================================
  664.  */
  665. void
  666. SetTextNode( OBJECT *tree, int parent, POP_PTR PopPtr, int obj, int Ypos, int textnum, int Width )
  667. {
  668.        ObNext( obj ) = -1;
  669.        ObHead( obj ) = -1;
  670.        ObTail( obj ) = -1;
  671.  
  672.        ObType( obj )   = G_STRING;
  673.        ObFlags( obj )  = NONE;
  674.        
  675.        ObString( obj ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * textnum ];
  676.  
  677.        ObState( obj )  = CmdState( PopPtr->CharPtr[ textnum ] );
  678.  
  679.        ObX( obj )      = 0;
  680.        ObY( obj )      = Ypos * gl_hchar;
  681.        ObW( obj )      = Width * gl_wchar;
  682.        ObH( obj )      = gl_hchar;
  683.        objc_add( tree, parent, obj );
  684. }
  685.  
  686.  
  687.  
  688.  
  689. /* CalcTextBufferSize()
  690.  * ====================================================================
  691.  * Calculate the text buffer size that we'll have to malloc, based upon
  692.  * the largest text string and the number of entries.
  693.  */
  694. long
  695. CalcTextBufferSize( POP_PTR PopPtr )
  696. {
  697.    char *xptr;
  698.    long size;
  699.    long xsize;
  700.    long actual;
  701.    int  i;
  702.    
  703.    size = 0L;
  704.    PWIDTH( PopPtr ) = 0;
  705.    xptr = ( char *)NULL;
  706.    xptr = PTEXT( PopPtr );
  707.    if( xptr )
  708.    {
  709.        for( i = 0; i < PNUM( PopPtr ); i++ )
  710.        {    
  711.          xsize = strlen( xptr );
  712.          actual = CheckForKeyCodes( PopPtr, xptr, i );
  713.          if( actual > size )
  714.             size = actual;
  715.          xptr += ( xsize + 1 );
  716.  
  717.          /* Get's us past the nulls to the head of the next string */
  718.          while( !( *xptr ) )
  719.            xptr++;         
  720.        }
  721.        /* size-> now contains the length of the longest string. */
  722.        size += 1;              /* for a blank after...*/
  723.        size += 2;           /* for 2 blanks before the string*/
  724.        /* Number of characters in our longest string...*/
  725.        PWIDTH( PopPtr ) = (int)size;
  726.  
  727.        size += 1;            /* for the null.       */
  728.        size += 10;            /* for the Control Keys or RT Arrow */
  729.        size *= PNUM( PopPtr );  /* For the number of entries. */
  730.        size += ( size / 2 );    /* Buffer area, just in case  */  
  731.    }
  732.    return( size );
  733. }
  734.  
  735.  
  736. /* BuildText()
  737.  * ====================================================================
  738.  * Takes the pointer to the menu_text given by the program and
  739.  * builds the text in the memory buffer where...
  740.  *  1) 2 spaces before each text string.
  741.  *  2) 1 space after the longest string.
  742.  *  3) pads spaces after shorter strings to match the longest string.
  743.  *  4) Null terminates each string.
  744.  *  5) *# - The char following is duplicated the width of the menu.
  745.  *        Characters following it are ignored to the end of the string.
  746.  *        example: *-    The line is filled with "---------"
  747.  *  6) @  - means to disable this menu item
  748.  *  7) /# - means that there is a keycode: ie: /T ==   [T]
  749.  *  8) ## - means that there is a function key.  #10 is F10
  750.  *  9) ^# - means that there is a ctrl key:  ^H
  751.  * 10) $# - means that there is a CmdKey: ie: $H = HELP ( 4 chars )
  752.  *     We ACKNOWLEDGE -----
  753.  *     [H] HELP    [U] Undo    [E] Escape    [I] Insert
  754.  *     [D] DEL        [C] Clr        [h] Home    [T] TAB
  755.  *     [e] ENTER    [R] RETURN
  756.  *
  757.  * 11) &# - means that there is an ALT Key:  &A - Alt A
  758.  * 12) ~# - means that there is a submenu: ~1 - attach submenu 1
  759.  * 13) !  - means that the menu item is CHECKED.
  760.  */
  761. void
  762. BuildText( POP_PTR PopPtr )
  763. {
  764.    char *sindex;        /* Pointer to source text...    */
  765.    char *stemp;            /* Pointer INTO source text     */
  766.    char *dindex;        /* Pointer to destination text  */
  767.    char *dtemp;            /* Pointer INTO destination text*/
  768.    int  num_entries;        /* Number of entries counter... */
  769.    int  num;
  770.    long length;            /* length of a string...    */
  771.    long i;            /* temp variable...        */
  772.    
  773.    if( !PTEXTBUFF( PopPtr ) )    /* if there is no destination buffer...*/
  774.        return;            /* skip doing all of this...           */
  775.           
  776.    sindex = PTEXT( PopPtr );
  777.    dindex = PTEXTBUFF( PopPtr );
  778.  
  779.    for( num_entries = 0; num_entries <= PNUM( PopPtr ); num_entries++ )
  780.    {
  781.      if( num_entries < PNUM( PopPtr ) )
  782.      {
  783.          strcpy( dindex, "  " );    /* Pad the entry with 2 blanks */
  784.          stemp = sindex;        /* Get a pointer to the source */
  785.          dtemp = dindex+2;        /* Get a pointer to the dest...*/
  786.  
  787.      /* Copy the source string to the destination buffer
  788.       * skipping the HOT chars that will allow submenus,
  789.       * disabling menus, keycodes etc.
  790.       */
  791.      while( *stemp )
  792.      {
  793.         switch( *stemp )
  794.         {
  795.            case '@': /* Disable menu item */
  796.            case '!': /* CheckMark menu item */
  797.                     stemp++;
  798.                     break;
  799.  
  800.            case '~': /* Submenu  */
  801.                     stemp += 2;
  802.                     break;
  803.  
  804.            default: *dtemp++ = *stemp++;
  805.                    break;
  806.         }
  807.      }
  808.  
  809.      /* Now add in the necessary text for the Keycodes..*/
  810.          switch( CmdFlag( PopPtr->CharPtr[ num_entries ] ) )
  811.          {
  812.           case '~': /* Submenu */
  813.             num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), 2 );
  814.             for( i = 0; i < num; i++ )                    
  815.                 *dtemp++ = ' ';     /* add a space */
  816.                     *dtemp++ = RIGHT_ARROW;    /* Right Arrow */   
  817.                     *dtemp++ = ' ';
  818.                     break;
  819.                              
  820.              default:
  821.                        break;
  822.      }
  823.      *dtemp = '\0';         
  824.       
  825.            /* Get the length of the destination string and
  826.             * pad it with blanks to make it the width of 
  827.             * the final string wanted.
  828.             */
  829.          length = strlen( dindex );
  830.          for( i = length; i < PWIDTH( PopPtr ); i++ )
  831.             strcat( dindex, " " );
  832.  
  833.        /* Get the length of the actual string and
  834.         * advance it to the next string skipping the NULL
  835.         * and moving to the first char of the next string.
  836.         */
  837.        length = strlen( sindex );
  838.        sindex = sindex + length + 1;
  839.        
  840.        /* Advance us thru the nulls to the start of the next string*/
  841.        while( !(*sindex) )
  842.          sindex++;
  843.  
  844.        /* Advance dest to end of the new string */   
  845.        dindex = dindex + PWIDTH( PopPtr ) + 1;
  846.      }
  847.      else
  848.      {
  849.        /* The PNUM() menu item will have an UP Arrow in its text */
  850.        strcpy( dindex, " " );
  851.        for( i = 1; i < PWIDTH( PopPtr ); i++ )
  852.           strcat( dindex, " " );  
  853.        *(dindex + 2 ) = UP_ARROW;    /* UP ARROW */
  854.           
  855.        /* The PNUM() + 1 menu item will have a DOWN Arrow in its text */
  856.        dindex = dindex + PWIDTH( PopPtr ) + 1;
  857.        strcpy( dindex, " " );
  858.        for( i = 1; i < PWIDTH( PopPtr ); i++ )
  859.           strcat( dindex, " " );  
  860.        *(dindex + 2 ) = DOWN_ARROW;    /* DOWN ARROW */       
  861.        
  862.        /* The PNUM() + 2 menu item will have a blank string in its text*/
  863.        dindex = dindex + PWIDTH( PopPtr ) + 1;
  864.        strcpy( dindex, " " );
  865.        for( i = 1; i < PWIDTH( PopPtr ); i++ )
  866.           strcat( dindex, " " );  
  867.      }     
  868.    }
  869. }
  870.  
  871.  
  872.  
  873. /* CountBlanksNeeded()
  874.  * ====================================================================
  875.  * Counts the number of blanks to go between the menu item and the
  876.  * control key or whatever.
  877.  */
  878. int
  879. CountBlanksNeeded( char *dindex, char *dtemp, int width, int num )
  880. {
  881.     long length;
  882.     
  883.     *dtemp   = '\0';
  884.     length = strlen( dindex );
  885.     return( width - 1 - (int)length - num );
  886. }
  887.  
  888.  
  889.  
  890.  
  891. /* Pop_Arrow()
  892.  *==========================================================================
  893.  * Scroll the popup menu items if up or down arrow is selected.
  894.  * 
  895.  * IN: int obj:        up or down arrow is selected.
  896.  *     int *offset:    Offset into the menu items text array
  897.  *     int num_items:   Total number of menu items involved.
  898.  *     char *items[]:   Pointer to the text array
  899.  *
  900.  * OUT: TRUE - ended on a submenu
  901.  *    FALSE - ended on a normal menu item.
  902.  */
  903. BOOLEAN
  904. Pop_Arrow( POP_PTR PopPtr, int obj )
  905. {
  906.    OBJECT *tree;
  907.    int    DrawFlag;
  908.    int    i,j;
  909.    GRECT  rect;
  910.    GRECT  xrect, clip;
  911.    MRETS  mk;
  912.    int    pxy[8];
  913.    int    xclip[4];
  914.    long   location = 0L;    /* SCREEN MFDB        */
  915.    BOOLEAN DelayFlag;
  916.    BOOLEAN ReturnFlag;
  917.                  
  918.    tree = POBJECT( PopPtr );
  919.  
  920.    xrect = ObRect( obj );
  921.    objc_offset( tree, obj, &xrect.g_x, &xrect.g_y );
  922.    DelayFlag  = TRUE;
  923.    ReturnFlag = FALSE;
  924.    
  925.    do
  926.    {
  927.        BSTATE = 0;
  928.        DrawFlag = FALSE;
  929.        /* Up Arrow Selected AND not at the top */
  930.        if( ( obj == PFIRST( PopPtr ) ) && ( POFFSET( PopPtr ) > 0 ))
  931.        {
  932.          POFFSET( PopPtr ) -= 1;
  933.          DrawFlag = TRUE;
  934.          /* special handling...to make it jump to zero if
  935.           * we are displaying item 2, we can jump to zero, blit and
  936.           * redraw the top 2 items instead.
  937.           */
  938.          if( POFFSET( PopPtr ) == 1 )
  939.              POFFSET( PopPtr ) = 0;    
  940.        }
  941.  
  942.        /* DOWN Arrow and not within PHEIGHT() items from the bottom */
  943.        if( ( obj == PLAST( PopPtr ) ) && ( POFFSET( PopPtr ) <= ( PNUM( PopPtr ) - PHEIGHT( PopPtr ) ) ) )
  944.        {
  945.          POFFSET( PopPtr ) += 1;
  946.          DrawFlag = TRUE;
  947.          /* Special handling for first item only */
  948.          if( POFFSET( PopPtr ) == 1 )
  949.          {
  950.            if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr ) - 1 ) < PNUM( PopPtr ) )
  951.                  POFFSET( PopPtr ) += 1;
  952.          }
  953.        }
  954.  
  955.        j = POFFSET( PopPtr );
  956.        if( DrawFlag )
  957.        {
  958.           UpArrowStatus( PopPtr );
  959.             DownArrowStatus( PopPtr );      
  960.  
  961.           /* Get the Blit Rectangle */
  962.           clip = ObRect( PFIRST( PopPtr ));
  963.           objc_offset( tree, ROOT, &clip.g_x, &clip.g_y );
  964.           clip.g_h *= PHEIGHT( PopPtr );
  965.       
  966.           /* Calculate the height */
  967.       
  968.           if( strchr( ObString( PFIRST( PopPtr ) ), UP_ARROW ) )
  969.           {
  970.              clip.g_h -= gl_hchar;    /* Subtract 1 char height     */
  971.              clip.g_y += gl_hchar;    /* Move the Ypos down 1 level */ 
  972.           }
  973.       
  974.           if( strchr( ObString( PLAST( PopPtr )), DOWN_ARROW ) )
  975.           {
  976.             clip.g_h -= gl_hchar;    /* Subtract 1 char height      */
  977.           }
  978.       
  979.           /* Set up SOURCE rectangle */
  980.           rc_intersect( &desk, &clip );
  981.           rect = clip;
  982.           rc_2xy( &clip, ( WORD *)&pxy[0] );   
  983.  
  984.           if( obj == PFIRST( PopPtr ) )
  985.               clip.g_y += gl_hchar;
  986.       
  987.           if( obj == PLAST( PopPtr ) )
  988.               clip.g_y -= gl_hchar;
  989.           rc_intersect( &desk, &clip );
  990.           rc_2xy( &clip, ( WORD *)&pxy[4] );   
  991.  
  992.           clip = rect;
  993.           rc_2xy( &clip, ( WORD *)&xclip[0] );   
  994.           vs_clip( vhandle, 1, xclip );
  995.           graf_mouse( M_OFF, 0L );
  996.           vro_cpyfm( vhandle, 3, pxy, ( MFDB *)&location, ( MFDB *)&location );
  997.           graf_mouse( M_ON, 0L );
  998.       
  999.             for( i = PFIRST( PopPtr ); i <= PLAST( PopPtr ); i++ )
  1000.             {
  1001.              if( ObType( i ) == G_STRING )
  1002.              {
  1003.                 if( i == PFIRST( PopPtr ) && strchr( ObString( PFIRST( PopPtr )), UP_ARROW ))
  1004.                 {
  1005.                     ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * PNUM( PopPtr ) ];
  1006.                     continue;
  1007.                 }
  1008.                     
  1009.                 if( i == PLAST( PopPtr ) &&  strchr( ObString( PLAST( PopPtr )), DOWN_ARROW ))
  1010.                 {
  1011.                     ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 1 ) ];
  1012.                     continue;
  1013.                 }    
  1014.                  
  1015.               SetNormal( i );
  1016.               if( CmdState( PopPtr->CharPtr[j] ) & CHECKED )
  1017.                   CheckObj( i );
  1018.                     
  1019.                 if( CmdState( PopPtr->CharPtr[j] ) & DISABLED )
  1020.                     Disable( i );
  1021.                       
  1022.                 ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * j++ ];
  1023.           }   
  1024.           }
  1025.   
  1026.       
  1027.           if( obj == PFIRST( PopPtr ) )
  1028.           {
  1029.              rect = ObRect( obj );
  1030.              objc_offset( tree, obj, &rect.g_x, &rect.g_y );
  1031.              if( strchr( ObString( obj ), UP_ARROW ) )
  1032.                  rect.g_y += gl_hchar;
  1033.              else
  1034.              {
  1035.                  rect.g_h += gl_hchar;
  1036.                  ObString( obj ) = &PTEXTBUFF( PopPtr )[ 0 ];
  1037.                  if( !(CmdState( PopPtr->CharPtr[ 0 ] ) & DISABLED) )
  1038.                    Select( obj );
  1039.                    
  1040.                  if( CmdState( PopPtr->CharPtr[ 0 ] ) & CHECKED )
  1041.                    CheckObj( obj );
  1042.              }   
  1043.           }     
  1044.       
  1045.           if( obj == PLAST( PopPtr ) )    
  1046.           {
  1047.              rect = ObRect( obj );
  1048.              objc_offset( tree, obj, &rect.g_x, &rect.g_y );
  1049.              rect.g_y -= gl_hchar;
  1050.              if( !strchr( ObString( obj ), DOWN_ARROW ) )
  1051.              {
  1052.            rect.g_h += gl_hchar;         
  1053.                ObString( obj ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) - 1 )];
  1054.                if( !(CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & DISABLED) )
  1055.                    Select( obj );
  1056.                if( CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & CHECKED )
  1057.                    CheckObj( obj );
  1058.              }   
  1059.           }
  1060.           Objc_draw( tree, ROOT, MAX_DEPTH, &rect ); 
  1061.        }
  1062.        else
  1063.        {
  1064.           if( ( obj == PFIRST( PopPtr ) )   &&
  1065.            ( !strchr( ObString( obj ), UP_ARROW )) &&
  1066.               ( !( CmdState( PopPtr->CharPtr[ 0 ] ) & DISABLED )) &&
  1067.               (  CmdFlag( PopPtr->CharPtr[0] ) == '~' )
  1068.             )
  1069.             ReturnFlag = TRUE;
  1070.  
  1071.           if( ( obj == PLAST( PopPtr ) )   &&
  1072.            ( !strchr( ObString( obj ), DOWN_ARROW ) ) &&
  1073.               ( !( CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & DISABLED )) &&
  1074.               (  CmdFlag( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) == '~' )
  1075.             )
  1076.             ReturnFlag = TRUE;
  1077.        }
  1078.        
  1079.        if( DelayFlag )
  1080.        {
  1081.            Evnt_timer( CLICK_DELAY );
  1082.            DelayFlag = FALSE;
  1083.        }    
  1084.  
  1085.        Graf_mkstate( &mk );  
  1086.    }while( (( BSTATE == 1 ) || ( BREAL == 1 )) && ( xy_inrect( mk.x, mk.y, &xrect )));
  1087.  
  1088.    Evnt_button( 1, 1, 0, &mk );       /* Make sure we have an up button...*/
  1089.    return( ReturnFlag );
  1090. }
  1091.  
  1092.  
  1093.  
  1094.  
  1095. /* Pop_Blit()
  1096.  * ====================================================================
  1097.  * Blit from screen to buffer or buffer to screen for Popup Box redraws.
  1098.  *
  1099.  * IN: long *PopPtr:    Pointer to memory buffer...
  1100.  *     GRECT *clip:    GRECT of clip blit area
  1101.  *     int   flag:    0 - blit from screen to buffer
  1102.  *                      1 - blit from buffer to screen
  1103.  *
  1104.  * OUT: returns TRUE if successful, FALSE if failed.
  1105.  */
  1106. BOOLEAN
  1107. Pop_Blit( long *PopPtr, GRECT *xclip, int flag )
  1108. {
  1109.    long     location = 0L;    /* SCREEN MFDB        */
  1110.    int      nplanes;        /* Number of planes    */
  1111.    unsigned long size;        /* size of malloc    */
  1112.    int      pxy[8];        /* pxy for blit        */
  1113.    GRECT    clip;
  1114.    MFDB     PopMFDB;        /* buffer MFDB        */
  1115.  
  1116.    clip      = ( GRECT )*xclip;
  1117.    clip.g_x -= 2;        /* adjust the clip area to take care of*/
  1118.    clip.g_y -= 2;        /* the edges and shadows...           */
  1119.    clip.g_w += 5;
  1120.    clip.g_h += 5;   
  1121.    rc_2xy( &clip, ( WORD *)&pxy[0] );
  1122.    vs_clip( vhandle, 1, pxy ); 
  1123.  
  1124.    vq_extnd( vhandle, 1, work_out );
  1125.    nplanes = work_out[4];
  1126.  
  1127.    if( !flag )                    /* screen to buffer blit*/
  1128.    {
  1129.      size = (unsigned long)(((long)clip.g_w + 7L )/8L) *
  1130.             (long)clip.g_h * (long)nplanes;
  1131.      size *= 2L;       
  1132.      *PopPtr = (long )malloc( (unsigned long ) size );
  1133.    }  
  1134.    
  1135.    if( !*PopPtr )
  1136.          return( FALSE );
  1137.  
  1138.    PopMFDB.fd_addr     = (long *)*PopPtr;    /* Setup the MFDB      */
  1139.    PopMFDB.fd_w        = clip.g_w;            
  1140.    PopMFDB.fd_h        = clip.g_h;
  1141.    PopMFDB.fd_wdwidth   = ( clip.g_w + 15 ) / 16;
  1142.    PopMFDB.fd_stand     = 0;
  1143.    PopMFDB.fd_nplanes   = nplanes;
  1144.    PopMFDB.fd_r1     = PopMFDB.fd_r2 = PopMFDB.fd_r3 = 0;
  1145.    
  1146.    graf_mouse( M_OFF, 0L );
  1147.    if(!flag )            
  1148.    { 
  1149.      /* Screen to buffer blit */  
  1150.      rc_intersect( &desk, &clip );        
  1151.      rc_2xy( &clip, ( WORD *)&pxy[0] );
  1152.      pxy[4] = pxy[5] = 0;           
  1153.      pxy[6] = clip.g_w - 1;
  1154.      pxy[7] = clip.g_h - 1;
  1155.      vro_cpyfm( vhandle, 3, pxy, ( MFDB *)&location, &PopMFDB );
  1156.    }
  1157.    else                
  1158.    {
  1159.      /* Buffer to screen blit */
  1160.      rc_intersect( &desk, &clip );
  1161.      pxy[0] = pxy[1] = 0;           
  1162.      pxy[2] = clip.g_w - 1; 
  1163.      pxy[3] = clip.g_h - 1;
  1164.      rc_2xy( &clip, ( WORD *)&pxy[4] );   
  1165.      vro_cpyfm( vhandle, 3, pxy, &PopMFDB, ( MFDB *)&location );
  1166.      if( *PopPtr )
  1167.          free( (long *)*PopPtr );
  1168.      *PopPtr = 0L;    
  1169.    }
  1170.    graf_mouse( M_ON, 0L );
  1171.  
  1172.    return( TRUE );
  1173. }
  1174.  
  1175.  
  1176.  
  1177.  
  1178. /* UpArrowStatus()
  1179.  * =====================================================================
  1180.  */
  1181. void
  1182. UpArrowStatus( POP_PTR PopPtr )
  1183. {
  1184.   OBJECT *tree;
  1185.   GRECT  rect;
  1186.     
  1187.   tree = POBJECT( PopPtr );
  1188.     
  1189.   /* Check if we need to activate the UP ARROW or deactivate it */
  1190.   if( POFFSET( PopPtr ) )    /* display the UP ARROW */
  1191.   {
  1192.      if( !strchr( ObString( PFIRST( PopPtr )), UP_ARROW ) )
  1193.      {
  1194.           rect = ObRect( PFIRST( PopPtr ) );
  1195.           objc_offset( tree, PFIRST( PopPtr ), &rect.g_x, &rect.g_y );
  1196.           
  1197.           SetNormal( PFIRST( PopPtr ) );
  1198.           ObString( PFIRST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * PNUM( PopPtr )  ];
  1199.           Objc_draw( tree, ROOT, MAX_DEPTH, &rect );
  1200.      }
  1201.   }
  1202.   else
  1203.     ObString( PFIRST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 2 ) ];
  1204. }
  1205.  
  1206.  
  1207.  
  1208. /* DownArrowStatus()
  1209.  * =====================================================================
  1210.  */
  1211. void
  1212. DownArrowStatus( POP_PTR PopPtr )
  1213. {
  1214.   OBJECT *tree;
  1215.   GRECT  rect;
  1216.     
  1217.   tree = POBJECT( PopPtr );
  1218.   
  1219.   /* Check if we need to display the DOWN ARROW */
  1220.  
  1221.   /* DISPLAY the DOWN ARROW */
  1222.   if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr )) <= PNUM( PopPtr ) )
  1223.   {
  1224.       if( !strchr( ObString( PLAST( PopPtr )), DOWN_ARROW ) )
  1225.       {
  1226.         rect = ObRect( PLAST( PopPtr ) );
  1227.         objc_offset( tree, PLAST( PopPtr ), &rect.g_x, &rect.g_y );
  1228.  
  1229.         SetNormal( PLAST( PopPtr ) );
  1230.         ObString( PLAST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 1 ) ];
  1231.         Objc_draw( tree, ROOT, MAX_DEPTH, &rect );
  1232.       }
  1233.   }
  1234.   else
  1235.     ObString( PLAST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 2 ) ];
  1236. }
  1237.  
  1238.  
  1239.  
  1240.  
  1241. /* SetArrowClickDelay()
  1242.  * =====================================================================
  1243.  * Set the Click Delay before the menu starts to scroll.
  1244.  */
  1245. void
  1246. SetArrowClickDelay( long delay )
  1247. {
  1248.    CLICK_DELAY = (( delay > 0L ) ? ( delay ) : ( INIT_CLICK_DELAY ));
  1249. }
  1250.  
  1251.  
  1252.  
  1253.  
  1254. /* CHECK FOR KEYCODES...
  1255.  * =====================================================================
  1256.  * Checks the string for the keystrings below so that they may be
  1257.  * subtracted from the length of the string for calculation purposes.
  1258.  */
  1259.  
  1260.  
  1261. /* CheckForDisable()
  1262.  * =====================================================================
  1263.  * Looks for a @ in the string.
  1264.  * Returns TRUE if found, FALSE if not.
  1265.  */
  1266. BOOLEAN
  1267. CheckForDisable( char *text )
  1268. {
  1269.    return( ( BOOLEAN )strchr( text, '@' ) );
  1270. }
  1271.  
  1272.  
  1273. /* CheckForMenuCheck()
  1274.  * =====================================================================
  1275.  * Looks for a ! in the string.
  1276.  * Returns TRUE if found, FALSE if not.
  1277.  */
  1278. BOOLEAN
  1279. CheckForMenuCheck( char *text )
  1280. {
  1281.   return( ( BOOLEAN )strchr( text, '!' ) );
  1282. }
  1283.  
  1284.  
  1285.  
  1286. /* CheckForSubMenu()
  1287.  * =====================================================================
  1288.  * Looks for a ~ in the string.
  1289.  * Returns the pointer to the string where it starts.
  1290.  */
  1291. char
  1292. *CheckForSubMenu( char *text )
  1293. {
  1294.    return( strchr( text, '~' ) );
  1295. }
  1296.  
  1297.  
  1298.  
  1299.  
  1300. /* CheckForKeyCodes()
  1301.  * =====================================================================
  1302.  * Checks the text string for various keycodes and subtracts
  1303.  * an appropriate amount from the length if any are found.
  1304.  * For many of these, there should only be one of them.
  1305.  * We then ADD back in the amount of ACTUAL text usage this item
  1306.  * would take. IE: a Function Key can be F10 so 3 characters...
  1307.  * or a CmdKey can be the letters: 'DEL' 3 characters added.
  1308.  * This will also fill up the CMDCHAR structures.
  1309.  */
  1310. long
  1311. CheckForKeyCodes( POP_PTR PopPtr, char *text, int num_entry )
  1312. {
  1313.     long length;
  1314.  
  1315.     length = strlen( text );
  1316.     if( strchr( text, '@' ))
  1317.        length -= 1L;
  1318.  
  1319.     if( strchr( text, '!' ))
  1320.        length -= 1L;
  1321.  
  1322.      if( CmdFlag( PopPtr->CharPtr[ num_entry ] )  == '~' )
  1323.          length += 1L;
  1324.          
  1325.     return( length );
  1326. }
  1327.  
  1328.  
  1329.  
  1330.  
  1331. /* END OF KEYCODE CHECKING
  1332.  * =====================================================================
  1333.  */
  1334.  
  1335.  
  1336. /* CONTROLLING THE APPEARANCE OF ITEMS
  1337.  * =====================================================================
  1338.  */
  1339.  
  1340. /* SetHeight()
  1341.  * =====================================================================
  1342.  * Set the height of the Menu.( in chars ).
  1343.  */
  1344. void
  1345. SetHeight( int MenuID, int Height )
  1346. {
  1347.    POP_PTR PopPtr;
  1348.    
  1349.    PopPtr = GetMenuPtr( MenuID );
  1350.    
  1351.    if( Height > 0 )
  1352.       PHEIGHT( PopPtr ) = Height;
  1353. }
  1354.  
  1355.  
  1356.  
  1357. /* SetNumItems()
  1358.  * =====================================================================
  1359.  * Set the number of items in the menu. This can be used to display
  1360.  * fewer items than the InsertMenu() call created.
  1361.  */
  1362. void
  1363. SetNumItems( int MenuID, int NumItems )
  1364. {
  1365.    POP_PTR PopPtr;
  1366.   
  1367.    PopPtr = GetMenuPtr( MenuID );
  1368.    if( NumItems > 0 )
  1369.       PNUM( PopPtr ) = NumItems;
  1370. }
  1371.  
  1372.  
  1373.  
  1374. /* CheckItem()
  1375.  * =====================================================================
  1376.  * Places or removes a check mark at the left of a menu item.
  1377.  */
  1378. void
  1379. CheckItem( int MenuID, int item, BOOLEAN check )
  1380. {
  1381.     POP_PTR PopPtr;
  1382.     
  1383.     PopPtr = GetMenuPtr( MenuID );
  1384.     if( check )
  1385.       CmdState( PopPtr->CharPtr[ item ] ) |= CHECKED;
  1386.     else
  1387.       CmdState( PopPtr->CharPtr[ item ] ) &= ~CHECKED;
  1388. }
  1389.  
  1390.  
  1391.  
  1392. /* DisableItem()
  1393.  * =====================================================================
  1394.  * Disables a menu item.
  1395.  */
  1396. void
  1397. DisableItem( int MenuID, int item )
  1398. {
  1399.    POP_PTR PopPtr;
  1400.    
  1401.    PopPtr = GetMenuPtr( MenuID );
  1402.    CmdState( PopPtr->CharPtr[ item ] ) |= DISABLED;
  1403. }
  1404.  
  1405.  
  1406.  
  1407. /* EnableItem()
  1408.  * =====================================================================
  1409.  * Enables a menu item.
  1410.  */
  1411. void
  1412. EnableItem( int MenuID, int item )
  1413. {
  1414.    POP_PTR PopPtr;
  1415.    
  1416.    PopPtr = GetMenuPtr( MenuID );
  1417.    CmdState( PopPtr->CharPtr[ item ] ) &= ~DISABLED;
  1418. }
  1419.  
  1420.  
  1421. /* SetItemCmd()
  1422.  * =====================================================================
  1423.  * This can be used to attach a submenu to a menu item.
  1424.  * You must still call 'SetSubMenuID()' tell the popups which
  1425.  * submenu to attach. Otherwise, this is a great way to change
  1426.  * the keyboard shortcuts. Use SetItemMark() to change the
  1427.  * keyboard shortcuts AFTER setting the type with the SetItemCmd().
  1428.  */
  1429. void
  1430. SetItemCmd( int MenuID, int item, char cmd ) 
  1431. {
  1432.    POP_PTR PopPtr;
  1433.    
  1434.    PopPtr = GetMenuPtr( MenuID );
  1435.    CmdFlag( PopPtr->CharPtr[ item ] ) = cmd;
  1436. }
  1437.  
  1438.  
  1439. /* GetItemCmd()
  1440.  * =====================================================================
  1441.  * This can be used to determine if a submenu is attached to 
  1442.  * this menu item. It returns the current command.
  1443.  * RETURNS: 
  1444.  *           '~' == SubMenu for example
  1445.  */
  1446. char
  1447. GetItemCmd( int MenuID, int item )
  1448. {
  1449.     POP_PTR PopPtr;
  1450.  
  1451.     PopPtr = GetMenuPtr( MenuID );
  1452.     return( CmdFlag( PopPtr->CharPtr[ item ] ) );
  1453. }
  1454.  
  1455.  
  1456.  
  1457. /* SetSubMenuID()
  1458.  * =====================================================================
  1459.  * This can be used to change the ID of the submenu associated
  1460.  * with a menu item. Nothing happens of course, if the menu_item 
  1461.  * does not have a submenu CMD.
  1462.  */
  1463. void
  1464. SetSubMenuID( int MenuID, int item, int ID )
  1465. {
  1466.    POP_PTR PopPtr;
  1467.  
  1468.    PopPtr = GetMenuPtr( MenuID );
  1469.    if( GetItemCmd( MenuID, item ) == '~' )
  1470.      CmdSubMenu( PopPtr->CharPtr[ item ] ) = ID;
  1471. }
  1472.  
  1473.  
  1474. /* GetSubMenuID()
  1475.  * =====================================================================
  1476.  * This can be used to get the ID of the submenu associated
  1477.  * with the menu item. Course, the item must HAVE a submenu in
  1478.  * the first place. Use GetItemCmd to determine if there is a
  1479.  * submenu.
  1480.  * RETURN: -1 if there is no submenu.
  1481.  */
  1482. int
  1483. GetSubMenuID( int MenuID, int item )
  1484. {
  1485.    POP_PTR PopPtr;
  1486.  
  1487.    PopPtr = GetMenuPtr( MenuID );
  1488.    if( GetItemCmd( MenuID, item ) == '~' )
  1489.      return( CmdSubMenu( PopPtr->CharPtr[ item ] ));
  1490.    else
  1491.      return( -1 );  
  1492. }
  1493.  
  1494.  
  1495.  
  1496. /* SetItem()
  1497.  * =====================================================================
  1498.  * Set the Menu item string.
  1499.  * 1) The string must not exceed the original string length.
  1500.  * 2) Meta characters will be printed as regular text.
  1501.  * 3) If the string is longer than the original, then
  1502.  *    the new string will be truncated.
  1503.  */
  1504. void
  1505. SetItem( int MenuID, int item, char *text )
  1506. {
  1507.    POP_PTR PopPtr;
  1508.    int     count;
  1509.    char    *txtptr;
  1510.    long    length;
  1511.    long    length2;
  1512.            
  1513.    PopPtr = GetMenuPtr( MenuID );
  1514.  
  1515.    txtptr = PTEXT( PopPtr );
  1516.    if( txtptr )
  1517.    {
  1518.        for( count = 0; count < item; count++ )
  1519.        {    
  1520.          length = strlen( txtptr );
  1521.          txtptr += ( length + 1 );
  1522.        }
  1523.        /* we've reached the text area that we want. */
  1524.        length = strlen( txtptr );
  1525.        strncpy( txtptr, text, length );
  1526.        length2 = strlen( txtptr );
  1527.        for( count = (int)length2; count < length; count++ )
  1528.           strcat( txtptr, " " );
  1529.    }
  1530. }
  1531.  
  1532.  
  1533.  
  1534. /* GetItem() 
  1535.  * =====================================================================
  1536.  * Returns the string associated with the menu item WITHOUT
  1537.  * the meta characters.
  1538.  */
  1539. char
  1540. *GetItem( int MenuID, int item )
  1541. {
  1542.    POP_PTR PopPtr;
  1543.    int     count;
  1544.    char    *txtptr;
  1545.    char    *sptr;
  1546.    char    *dptr;
  1547.    long    length;
  1548.             
  1549.    PopPtr = GetMenuPtr( MenuID );
  1550.  
  1551.    txtptr = PTEXT( PopPtr );
  1552.    if( txtptr )
  1553.    {
  1554.        for( count = 0; count < item; count++ )
  1555.        {    
  1556.          length = strlen( txtptr );
  1557.          txtptr += ( length + 1 );
  1558.        }
  1559.        
  1560.        /* we've reached the text area that we want. */
  1561.        length = strlen( txtptr );
  1562.        sptr = txtptr;
  1563.        dptr = &TempString[0];
  1564.        
  1565.        while( *sptr )
  1566.        {
  1567.        
  1568.           switch( *sptr )
  1569.           {
  1570.                  case '@':
  1571.               case '!': sptr++;
  1572.                     break;
  1573.  
  1574.               case '~':
  1575.             sptr += 2;
  1576.                    break;
  1577.  
  1578.                  default:  *dptr++ = *sptr++;
  1579.                            break;
  1580.            }
  1581.        }
  1582.        *dptr = '\0';
  1583.        return( ( char *)&TempString[0] );
  1584.    }
  1585.    return( (char *)NULL );
  1586. }
  1587.  
  1588.  
  1589.  
  1590. /* SetStartItem()
  1591.  * =====================================================================
  1592.  * This can be used to Set the start item on a submenu. This will
  1593.  * work of course, on the main menu too.
  1594.  */
  1595. void
  1596. SetStartItem( int MenuID, int item )
  1597. {
  1598.    POP_PTR PopPtr;
  1599.    
  1600.    PopPtr = GetMenuPtr( MenuID );
  1601.    PopUpItem( PopPtr ) = item;
  1602. }
  1603.  
  1604.  
  1605.  
  1606. /* GetStartItem()
  1607.  * =====================================================================
  1608.  * This can be used to get the start item in a submenu. Or at least
  1609.  * wot it currently thinks it is...
  1610.  */
  1611. int
  1612. GetStartItem( int MenuID )
  1613. {
  1614.    POP_PTR PopPtr;
  1615.    
  1616.    PopPtr = GetMenuPtr( MenuID );
  1617.    return( PopUpItem( PopPtr ) );
  1618. }
  1619.  
  1620.  
  1621.  
  1622. /* END OF APPEARANCE OF ITEMS
  1623.  * =====================================================================
  1624.  */
  1625.  
  1626.  
  1627. /* SUBMENU HANDLING
  1628.  * =====================================================================
  1629.  */
  1630.  
  1631.  
  1632.  
  1633. /* DoSubMenu()
  1634.  * =====================================================================
  1635.  * Display the submenu if necessary...
  1636.  *
  1637.  */
  1638. POP_PTR
  1639. DoSubMenu( POP_PTR PopPtr, int obj )
  1640. {
  1641.    int        num;
  1642.    POP_PTR SubPopPtr;
  1643.    GRECT   SubRect;   
  1644.    int     MenuID;
  1645.    OBJECT  *tree;
  1646.    int     curx;
  1647.    
  1648.  
  1649.    ActiveTree( POBJECT( PopPtr ) );
  1650.    SubPopPtr = ( POP_PTR )NULL;
  1651.    if( obj != -1 )
  1652.    {
  1653.      num = FindNum( PopPtr, obj );
  1654.      if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' )
  1655.      {
  1656.         SubRect   = ObRect( obj );
  1657.         objc_offset( tree, obj, &SubRect.g_x, &SubRect.g_y );
  1658.       
  1659.         MenuID    = CmdSubMenu( PopPtr->CharPtr[ num ] );
  1660.         SubPopPtr = GetMenuPtr( MenuID );
  1661.         /* Displays faster if drawn on a byte boundary.*/
  1662.         curx = SubRect.g_x + SubRect.g_w -1 - gl_wchar;
  1663.         curx = (( curx + 7 )/8)*8;
  1664.         if( !ShowSubMenu( MenuID, curx, SubRect.g_y, &SubRect ))
  1665.            SubPopPtr = ( POP_PTR )NULL;
  1666.      }
  1667.    }  
  1668.    return( SubPopPtr );
  1669. }
  1670.  
  1671.  
  1672.  
  1673.  
  1674. /* ShowSubMenu()
  1675.  * =====================================================================
  1676.  * Display the Initial Submenu 
  1677.  * RETURN: TRUE - AOK
  1678.  *         FALSE - Memory allocation error.
  1679.  */
  1680. BOOLEAN
  1681. ShowSubMenu( int MenuID, int xpos, int ypos, GRECT *rect )
  1682. {
  1683.     POP_PTR PopPtr;
  1684.     int     max_height;
  1685.            
  1686.     PopPtr = GetMenuPtr( MenuID );
  1687.  
  1688.     PPREV( PopPtr ) = ( POP_PTR )NULL;
  1689.         
  1690.     PXPOS( PopPtr )   = xpos;
  1691.     PYPOS( PopPtr )   = ypos;
  1692.  
  1693.     /* Take care of height of menu */ 
  1694.     if( PHEIGHT( PopPtr ) > PNUM( PopPtr ) )
  1695.         PHEIGHT( PopPtr ) = PNUM( PopPtr );
  1696.  
  1697.     /* Limit Height to Height of screen - ( 2 * gl_hchars ) */
  1698.     max_height = yres - ( 2 * gl_hchar );
  1699.     while( ( PHEIGHT( PopPtr ) * gl_hchar ) > max_height )
  1700.        PHEIGHT( PopPtr ) -= 1;
  1701.  
  1702.     PIX_HEIGHT( PopPtr )  = ( PHEIGHT( PopPtr ) * gl_hchar );
  1703.  
  1704.     /* Take care of the width of the menu here...*/
  1705.     PTEXTBUFFSIZE( PopPtr ) = CalcTextBufferSize( PopPtr );
  1706.     PIX_WIDTH( PopPtr )     = ( PWIDTH( PopPtr ) * gl_wchar );
  1707.  
  1708.     AdjustToScreen( PopPtr, xpos, ypos, rect, TRUE, FALSE );
  1709.  
  1710.     if( Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 0 ) )
  1711.     {
  1712.         if( Build_Objects( PopPtr ))
  1713.           return( TRUE );
  1714.     }    
  1715.     return( FALSE );
  1716. }
  1717.  
  1718.  
  1719.  
  1720.  
  1721. /* HideSubMenu()
  1722.  * =====================================================================
  1723.  * Hides the Submenu and free's any memory that it malloc'ed.
  1724.  */
  1725. void
  1726. HideSubMenu( POP_PTR PopPtr )
  1727. {
  1728.     Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 1 );
  1729.  
  1730.     if( POBJECT( PopPtr ) )
  1731.         free( POBJECT( PopPtr ) );
  1732.         
  1733.     if( PTEXTBUFF( PopPtr ) )    
  1734.         free( PTEXTBUFF( PopPtr ) );
  1735.         
  1736.     POBJECT( PopPtr )   = ( OBJECT *)NULL;
  1737.     PTEXTBUFF( PopPtr ) = ( char *)NULL;
  1738. }
  1739.  
  1740.  
  1741.  
  1742.  
  1743. /* EvntSubMenu()
  1744.  * ====================================================================
  1745.  * Submenu Handling...
  1746.  * IN: POP_PTR PopPtr
  1747.  * OUT: long      -1L   No Menu Items selected.
  1748.  *                -2L   We've rentered the submenu ( won't get to user )
  1749.  *                High Word         Low Word
  1750.  *                ---------        --------
  1751.  *                 Menu ID            -1      Selected Disabled Item
  1752.  *           Menu ID        Menu Item Selected Menu Item.
  1753.  */
  1754. long
  1755. EvntSubMenu( POP_PTR PopPtr )
  1756. {
  1757.    long   result;            /* Result to return with...     */
  1758.    MRETS  mk;                /* mouse structures          */
  1759.    int    oldobj;            /* old object              */
  1760.    int    done = FALSE;            /* Done flag to return..    */
  1761.    int    obj;                /* object mouse is over...      */
  1762.    int    event;            /* Mouse button event flag    */
  1763.                        /* TRUE = event occurred    */
  1764.    POP_PTR SubPopPtr;            /* Pointer to Submenu...        */
  1765.    POP_PTR TempPtr;            /* Temporary Submenu Pointer    */
  1766.    
  1767.    GRECT  SubRect;            /* ObRect of Submenu        */
  1768.    GRECT  TempRect;            /* Temp ObRect                */
  1769.    
  1770.    GRECT  wall;               /* ObRect for boundary checks   */
  1771.    GRECT  BoxRect;            /* ObRect for boundary checks   */
  1772.    GRECT  DragRect;            /* ObRect for Drag boundary     */
  1773.  
  1774.    OBJECT *tree;            /* Object tree declaration      */
  1775.    int    Action;            /* Action that is occurring     */
  1776.    int    num;                /* temp number storage        */
  1777.    int    ActiveObj;            /* Current Active Object        */
  1778.    
  1779.    long CurTimeHz;            /* Time in 200Hz...*/
  1780.    BOOLEAN MenuDelayFlag;        /* Display Submenu Delay in effect*/
  1781.    BOOLEAN MenuDragFlag;        /* Drag Submenu Delay in effect  */
  1782.    BOOLEAN DragStart;            /* Start Drag Submenu Delay...   */
  1783.    int  MenuObject;            /* Object to start the timer on*/            
  1784.    int  OldY;                /* Old Y pos for a drag */   
  1785.    int  OldX;
  1786.          
  1787.    ActiveTree( POBJECT( PopPtr ) );
  1788.    Graf_mkstate( &mk );
  1789.  
  1790.    oldobj     = -1;
  1791.    obj        = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y );
  1792.    SubPopPtr  = ( POP_PTR )NULL;
  1793.    BoxRect    = ObRect( ROOT );
  1794.    result     = -1L;
  1795.    OldY          = 0;
  1796.    OldX       = 0;
  1797.       
  1798.    MenuDelayFlag = FALSE;    
  1799.    MenuDragFlag  = FALSE;      
  1800.    DragStart     = FALSE;
  1801.    ActiveObj     = -1;
  1802.    BSTATE      = BREAL = 0;
  1803.  
  1804.    do
  1805.    {
  1806.        if( obj != -1 )
  1807.        {    
  1808.        if( obj != oldobj )
  1809.           {
  1810.          wall = ObRect( obj );
  1811.          objc_offset( tree, obj, &wall.g_x, &wall.g_y );
  1812.           if( SubPopPtr )
  1813.             {
  1814.             PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  1815.             HideSubMenu( SubPopPtr );
  1816.             SubPopPtr = ( POP_PTR )NULL;
  1817.             MenuDragFlag  = FALSE;
  1818.             MenuDelayFlag = FALSE;
  1819.             DragStart     = FALSE;
  1820.             ActiveObj = -1;
  1821.          }
  1822.          if(( oldobj != -1 ) && ( oldobj != obj ))
  1823.               deselect( tree, oldobj );
  1824.  
  1825.          if( ( obj == PFIRST( PopPtr ) && strchr( ObString( obj ), UP_ARROW ) ) ||
  1826.              ( obj == PLAST( PopPtr ) && strchr( ObString( obj ), DOWN_ARROW ) )
  1827.            )
  1828.            deselect( tree, obj );  
  1829.          else
  1830.          {
  1831.             if( !IsDisabled( obj ) ) 
  1832.             {
  1833.                   select( tree, obj );
  1834.  
  1835.                if( ( num = FindNum( PopPtr, obj )) != -1 )
  1836.            {
  1837.               if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' )
  1838.               {
  1839.                  Supexec( GetTimeHz );
  1840.                  CurTimeHz     = TimeInHz;
  1841.                  MenuDelayFlag = TRUE;
  1842.                  MenuDragFlag  = FALSE;
  1843.                      DragStart     = FALSE;
  1844.                  MenuObject    = obj;
  1845.               }  
  1846.            }                
  1847.                }  
  1848.             }  
  1849.        }
  1850.        }
  1851.        else
  1852.        {
  1853.      if(( oldobj != -1 ) && ( oldobj != obj ) )
  1854.          {
  1855.         deselect( tree, oldobj );
  1856.          }
  1857.        }
  1858.        oldobj = obj;
  1859.        
  1860.        Graf_mkstate( &mk );
  1861.        OldY = mk.y;
  1862.        OldX = mk.x;
  1863.        do
  1864.        {
  1865.                BSTATE = 0;
  1866.             if( MenuDragFlag )
  1867.             {
  1868.                if( ( !xy_inrect( mk.x, mk.y, &wall ) || 
  1869.                    ( SubPopPtr && xy_inrect( mk.x, mk.y, &SubRect ) ))
  1870.                  )
  1871.                {
  1872.           if( DragStart )
  1873.           {
  1874.                     Supexec( GetTimeHz );
  1875.                     CurTimeHz = TimeInHz;
  1876.                     DragStart = FALSE;
  1877.                     DragRect.g_x = mk.x;
  1878.                     DragRect.g_y = SubRect.g_y;
  1879.                     DragRect.g_h = SubRect.g_h;
  1880.                     
  1881.                     OldX = mk.x;
  1882.                     OldY = mk.y;
  1883.                                 
  1884.                     if( mk.x <= SubRect.g_x )
  1885.                     {
  1886.                        DragRect.g_w = SubRect.g_w + ( SubRect.g_x - DragRect.g_x );
  1887.                     }
  1888.                     else
  1889.                     {
  1890.                DragRect.g_x = SubRect.g_x;
  1891.                        DragRect.g_w = mk.x - SubRect.g_x + 1;
  1892.                     }
  1893.                   }  
  1894.  
  1895.               if( SubPopPtr )
  1896.                   {
  1897.                        if( !DragStart )
  1898.                        {
  1899.                  Supexec( GetTimeHz );
  1900.                  if( ( TimeInHz - CurTimeHz ) >= SUBDRAG_DELAY )
  1901.                      MenuDragFlag = FALSE;
  1902.  
  1903.                  if( !xy_inrect( mk.x, mk.y, &DragRect ) ||
  1904.                      (xy_inrect( mk.x, mk.y, &DragRect ) &&
  1905.                       ( mk.x == OldX ) &&
  1906.                       ( mk.y != OldY )
  1907.                  ) 
  1908.                    )  
  1909.                    MenuDragFlag = FALSE;    
  1910.                  else
  1911.                  {
  1912.                         DragRect.g_x = mk.x;
  1913.                         if( mk.x <= SubRect.g_x )
  1914.                         {
  1915.                                   DragRect.g_w = SubRect.g_w + ( SubRect.g_x - DragRect.g_x );
  1916.                             }
  1917.                             else
  1918.                            {
  1919.                        DragRect.g_x = SubRect.g_x;
  1920.                                DragRect.g_w = mk.x - SubRect.g_x + 1;
  1921.                             }
  1922.                  }    
  1923.                }      
  1924.  
  1925.                        /* We're in the new submenu...*/
  1926.                        if( xy_inrect( mk.x, mk.y, &SubRect ) )
  1927.                        {
  1928.                          Action = 0;
  1929.                          MenuDragFlag = FALSE;
  1930.                          break;    
  1931.                        }    
  1932.                        MenuDelayFlag = FALSE;
  1933.                   }
  1934.                }
  1935.             }
  1936.             else
  1937.         {            
  1938.                 BoxRect = ObRect( ROOT );
  1939.                 if( xy_inrect( mk.x, mk.y, &BoxRect ) )
  1940.                 {
  1941.                        /* We're in our current submenu still
  1942.                      * but we might be in a new menu item.
  1943.                      */
  1944.                        Action = 1;
  1945.                        break;
  1946.                 }      
  1947.                 else           
  1948.                 {
  1949.                     /* Checking to see if we're in any of the previous menus */
  1950.                     TempPtr = PPREV( PopPtr );
  1951.                     while( TempPtr )
  1952.                     {
  1953.                        /* We're in a previous submenu or root */
  1954.                        TempRect = PObRect( TempPtr );
  1955.                        if( xy_inrect( mk.x, mk.y, &TempRect ) )
  1956.                        {
  1957.                           Action = 2;
  1958.                           goto abort;
  1959.                    }
  1960.                    TempPtr = PPREV( TempPtr );
  1961.                     }
  1962.                 }
  1963.                 
  1964.                 /* if we reach here, we're outside of all the rectangles.*/
  1965.             if( SubPopPtr )
  1966.             {
  1967.                    HideSubMenu( SubPopPtr );
  1968.                PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  1969.                SubPopPtr     = ( POP_PTR )NULL;
  1970.                ActiveObj     = -1;
  1971.                MenuDragFlag  = FALSE;
  1972.                MenuDelayFlag = FALSE;
  1973.                 }
  1974.  
  1975.             if( oldobj != -1 )
  1976.             {
  1977.               deselect( tree, oldobj );
  1978.               oldobj = obj = -1;
  1979.             }  
  1980.             }
  1981.             Action = -1;
  1982.  
  1983.             OldX = mk.x;
  1984.             OldY = mk.y;
  1985.             Evnt_timer( 0L );         /* Kludge to put in a delay 
  1986.                               * so that we can get a new mouse
  1987.                               * x,y with > 1 pixel difference
  1988.                               */
  1989.             Graf_mkstate( &mk );
  1990.     }while( !BSTATE );
  1991. abort:
  1992.        /* we've exited due to a button click, OR
  1993.         * we've entered one of the rectangles.
  1994.         * Let's see wot we've got.
  1995.         * If Action == -1, then the user clicked outside of
  1996.         * ANY rectangles and we should just exit and close
  1997.         * down all of the menus.
  1998.         */
  1999.        Graf_mkstate( &mk );
  2000.        obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y );
  2001.  
  2002.        /* Fix for when the submenu is displayed and we click
  2003.         * on its title to exit.
  2004.         */
  2005.        if(  xy_inrect( mk.x, mk.y, &wall ) &&
  2006.             SubPopPtr && MenuDragFlag && ( BSTATE == 1 ) )
  2007.         Action = 1;            
  2008.           
  2009.        event = FALSE;
  2010.        switch( Action )
  2011.        {
  2012.           case 0:  /* Entered the New Submenu, if there is one */
  2013.                if( SubPopPtr )
  2014.                {
  2015.                   /* need to check wot is returned...*/
  2016.                   result = EvntSubMenu( SubPopPtr );
  2017.                   if(( result == -1L ) || ( result != -2L ) )
  2018.                   {
  2019.                      obj = -1;
  2020.                      event = TRUE;
  2021.                      goto leave;
  2022.                   }
  2023.                   TempPtr = SubPopPtr;   
  2024.                   PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  2025.                   HideSubMenu( SubPopPtr );
  2026.                   SubPopPtr     = ( POP_PTR )NULL;
  2027.                   MenuDragFlag  = FALSE;
  2028.               MenuDelayFlag = FALSE;
  2029.  
  2030.               Graf_mkstate( &mk );
  2031.                       obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y );
  2032.                   num = FindNum( PopPtr, obj );
  2033.                      if( ( obj != -1 ) && ( ActiveObj == obj ) &&
  2034.                          ( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' ) &&
  2035.                          ( GetMenuPtr( CmdSubMenu( PopPtr->CharPtr[ num ] )) == TempPtr )
  2036.                        )
  2037.               {
  2038.                   if( !IsSelected( obj ) )
  2039.                 select( tree, obj );
  2040.  
  2041.                   if( ( SubPopPtr = DoSubMenu( PopPtr, obj )) != ( POP_PTR )NULL )
  2042.                         {
  2043.                           ActiveObj = obj;
  2044.                       SubRect = PObRect( SubPopPtr );
  2045.               ActiveTree( POBJECT( PopPtr ) );
  2046.                       PPREV( SubPopPtr ) = PopPtr;
  2047.                           MenuDragFlag       = TRUE;
  2048.                           DragStart          = TRUE;
  2049.                    wall = ObRect( obj );
  2050.                   objc_offset( tree, obj, &wall.g_x, &wall.g_y );
  2051.                     }  
  2052.               }
  2053.               else
  2054.                 ActiveObj = -1;
  2055.                }     
  2056.                  break;
  2057.                   
  2058.           case 1:  /* we are still in the current submenu...*/
  2059.                    /* The user click on an arrow button? */
  2060.                   if( (( BSTATE == 1 ) || ( BREAL == 1 )) && (( obj == PFIRST( PopPtr)  && strchr( ObString( obj ), UP_ARROW )) ||
  2061.                        (( obj == PLAST( PopPtr ) ) && strchr( ObString( obj ), DOWN_ARROW ))
  2062.                      )) 
  2063.                    {
  2064.                     if( Pop_Arrow( PopPtr, obj ) )
  2065.                       {
  2066.                  if( ( num = FindNum( PopPtr, obj )) != -1 )
  2067.                  {
  2068.                    if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' )
  2069.                    {
  2070.                      Supexec( GetTimeHz );
  2071.                      CurTimeHz     = TimeInHz;
  2072.                      MenuDelayFlag = TRUE;
  2073.                      MenuObject    = obj;
  2074.                    }  
  2075.                  }                
  2076.                       }
  2077.                   }
  2078.                   else
  2079.                   {
  2080.                      if( !SubPopPtr && MenuDelayFlag && ( MenuObject == obj ))
  2081.                      {
  2082.                        Supexec( GetTimeHz );
  2083.                        if( ( TimeInHz - CurTimeHz ) >= SUBMENU_DELAY )
  2084.                        {
  2085.                             MenuDelayFlag = FALSE;
  2086.                  if( !IsSelected( obj ) )
  2087.                       select( tree, obj );
  2088.  
  2089.                       if( ( SubPopPtr = DoSubMenu( PopPtr, obj )) != ( POP_PTR )NULL )
  2090.                      {
  2091.                        ActiveObj = obj;
  2092.                            SubRect   = PObRect( SubPopPtr );
  2093.                    ActiveTree( POBJECT( PopPtr ) );
  2094.                            PPREV( SubPopPtr ) = PopPtr;
  2095.                            MenuDragFlag       = TRUE;
  2096.                            DragStart          = TRUE;
  2097.                        wall = ObRect( obj );
  2098.                        objc_offset( tree, obj, &wall.g_x, &wall.g_y );
  2099.                          }     
  2100.                            
  2101.                        }    
  2102.                      }
  2103.                      else
  2104.                        MenuDelayFlag = FALSE;
  2105.                            
  2106.                      /* we are just in another menu item.
  2107.                       * If a submenu was displayed, hide it.
  2108.                       */
  2109.               if( SubPopPtr && ( ActiveObj != obj ) )
  2110.               {
  2111.                     PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  2112.                         HideSubMenu( SubPopPtr );
  2113.                     SubPopPtr     = ( POP_PTR )NULL;
  2114.                 MenuDragFlag  = FALSE;
  2115.                 MenuDelayFlag = FALSE;
  2116.                     ActiveObj     = -1;
  2117.                   }
  2118.                   
  2119.                   /* if the buttons are down, they clicked on something*/
  2120.                   if(( BSTATE == 1 ) || ( BREAL == 1 ))
  2121.                   {
  2122.                       event = TRUE;
  2123.                       
  2124.                           obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y );
  2125.                           
  2126.                           CurMenu   = PMENUID( PopPtr );
  2127.                           CurObject = obj;
  2128.                           result    = 0L;
  2129.                   }
  2130.                   }
  2131.                  break;
  2132.                  
  2133.           case 2:  /* we have entered a previous submenu...  */
  2134.            result = -2L;
  2135.  
  2136.           case -1: /* we have clicked outside of ANY submenus.*/
  2137.                  if( Action == -1 )
  2138.                  {
  2139.                     /* don't exit if not a left button click */
  2140.                     if( BSTATE != 1 )
  2141.                       goto leave;
  2142.                  }
  2143.                  CurMenu = CurObject = -1;
  2144.           default: event = TRUE;
  2145.            obj    = -1;
  2146.                  break;
  2147.        }
  2148.  
  2149. leave:              
  2150.        if( event )
  2151.        {
  2152.           if( SubPopPtr )
  2153.           {
  2154.              PPREV( SubPopPtr ) = ( POP_PTR )NULL;
  2155.              HideSubMenu( SubPopPtr );
  2156.                SubPopPtr = ( POP_PTR )NULL;
  2157.           }
  2158.       done = TRUE;
  2159.        }     
  2160.  
  2161.    }while( !done );
  2162.  
  2163.          
  2164.    if(( obj != -1 ) && ( result != -2L ) && ( result != -1L ) )
  2165.    {
  2166.       result = 0L;
  2167.       result = ( int )PMENUID( PopPtr );
  2168.       result = ( result << 16L );
  2169.       
  2170.       if( !IsDisabled( obj ) )
  2171.         obj = FindNum( PopPtr, obj );
  2172.       else
  2173.         obj = -1;
  2174.       result |= ( obj & 0x0000FFFF );
  2175.    }
  2176.    return( result );
  2177. }
  2178.  
  2179.  
  2180.  
  2181. /* FindNum()
  2182.  * =====================================================================
  2183.  * Given the object #, find the adjusted index of the menu item.
  2184.  */
  2185. int
  2186. FindNum( POP_PTR PopPtr, int obj )
  2187. {
  2188.     int offset;
  2189.     
  2190.     offset = POFFSET( PopPtr );
  2191.     if( offset > 0 )
  2192.         offset -= 1;
  2193.     obj -= PFIRST( PopPtr );
  2194.     obj += offset;    
  2195.     return( obj );    
  2196. }
  2197.  
  2198.  
  2199.  
  2200.  
  2201. /* MenuChoice()
  2202.  * =====================================================================
  2203.  * Returns the CURRENT values of the menu and object clicked on.
  2204.  * Call ONLY after the PopMenuSelect() call has returned.
  2205.  */
  2206. long
  2207. MenuChoice( void )
  2208. {
  2209.    long value;
  2210.    POP_PTR PopPtr;
  2211.    int obj;
  2212.    
  2213.    value = 0L;
  2214.    value = ( int )CurMenu;
  2215.    value = ( value << 16L );
  2216.    
  2217.    if( CurMenu != -1 )
  2218.    {
  2219.       PopPtr = GetMenuPtr( CurMenu );   
  2220.       obj = FindNum( PopPtr, CurObject );
  2221.    }     
  2222.    else
  2223.       obj = -1;
  2224.    value |= ( obj & 0x0000FFFF );
  2225.    return( value );
  2226. }
  2227.  
  2228.  
  2229. /* END OF SUBMENU ROUTINES
  2230.  * =====================================================================
  2231.  */
  2232.  
  2233.  
  2234.  
  2235.  
  2236.  
  2237. /* SUBMENU DELAY ROUTINES
  2238.  * =====================================================================
  2239.  */
  2240.  
  2241. /* GetTimeHz()
  2242.  * =====================================================================
  2243.  * Gets the System time in 20ms ticks.
  2244.  */
  2245. long
  2246. GetTimeHz( void )
  2247. {
  2248.    long *ptr;
  2249.    
  2250.    ptr = ( long *)0x4BA;
  2251.    TimeInHz = *ptr;
  2252.    return( TimeInHz );
  2253. }
  2254.  
  2255.  
  2256.  
  2257.  
  2258. /* SetSubMenuDelay()
  2259.  * =====================================================================
  2260.  * Sets the length of time before a submenu appears as the user drags
  2261.  * the pointer thru a menu list.
  2262.  * The Units are in # of Milliseconds. ( 1000 = 1 second ).
  2263.  */
  2264. void
  2265. SetSubMenuDelay( long ms )
  2266. {
  2267.    if( ms < 0L )
  2268.        ms = INIT_DISPLAY_DELAY;
  2269.    if( ms )    
  2270.        ms /= 20L;     
  2271.    SUBMENU_DELAY = ms;
  2272. }
  2273.  
  2274.  
  2275.  
  2276.  
  2277. /* SetSubDragDelay()
  2278.  * =====================================================================
  2279.  * Sets the length of time the user has to drag diagonally, a pointer
  2280.  * to an active submenu before the submenu closes.
  2281.  */
  2282. void
  2283. SetSubDragDelay( long ms )
  2284. {
  2285.    if( ms < 0L )
  2286.        ms = INIT_DRAG_DELAY;
  2287.    if( ms )    
  2288.        ms /= 20L;
  2289.    SUBDRAG_DELAY = ms;
  2290. }
  2291.  
  2292.  
  2293. /* END OF SUBMENU DELAY ROUTINES
  2294.  * =====================================================================
  2295.  */
  2296.  
  2297.  
  2298.  
  2299. /* AdjustToScreen()
  2300.  * =====================================================================
  2301.  * Takes the Submenu XY and adjusts it so that it fits within
  2302.  * the screen area still. If there is an optional object, we will
  2303.  * adjust around that.
  2304.  * If we DO adjust, we'll try to get it on a byte boundary.
  2305.  * BOOLEAN byte_flag - a flag to align the xpos on a byte boundary.
  2306.  *                   - TRUE - YES!
  2307.  *             - NOTE: if the menu does not want byte adjusting
  2308.  *                 BUT it is off the screen, we will adjust
  2309.  *                 it back ONTO the screen on a byte boundary.
  2310.  *                 We just won't byte_adjust it if its already
  2311.  *                 on the screen in its entirety.
  2312.  * BOOLEAN Display_Flag; - a flag for vertical alignment of submenus.
  2313.  *             - When the submenu extends BELOW the bottom
  2314.  *               of the screen, there are 2 ways to adjust
  2315.  *               the menu.
  2316.  *             1)Adjust by displaying the menu upwards vertically
  2317.  *               from the rectangle button.
  2318.  *             2)Adjust by moving the menu upwards 1 gl_hchar
  2319.  *               at a time until the menu does not extend
  2320.  *               below the screen boundary.
  2321.  *             TRUE - Use Version 1
  2322.  *             FALSE - Use Version 2
  2323.  */
  2324. void
  2325. AdjustToScreen( POP_PTR PopPtr, int xpos, int ypos, GRECT *rect, BOOLEAN byte_flag, BOOLEAN Display_Flag )
  2326. {
  2327.     int temp;
  2328.     int position;
  2329.            
  2330.     /* Handle the X Coordinate Adjustments...
  2331.      *------------------------------------------------------------------
  2332.      */
  2333.     temp = xpos + PIX_WIDTH( PopPtr );
  2334.     if( temp > ( xres - 4 ))
  2335.     {
  2336.         if( !byte_flag )
  2337.         {
  2338.           temp -= ( xres - 4 );
  2339.           temp = ( temp + gl_wchar )/gl_wchar;
  2340.           temp *= gl_wchar;
  2341.           xpos -= temp;
  2342.         }
  2343.         else
  2344.         {
  2345.           xpos = rect->g_x - PIX_WIDTH( PopPtr );
  2346.           xpos = ( xpos + gl_wchar )/gl_wchar;
  2347.           xpos *= gl_wchar;
  2348.         }        
  2349.     }
  2350.     else
  2351.     {
  2352.         if( byte_flag )
  2353.         {
  2354.           xpos = ( xpos + ( gl_wchar - 1 ) )/gl_wchar;
  2355.           xpos *= gl_wchar;
  2356.         }     
  2357.     }
  2358.     
  2359.     /* Handle X limits < 0 */
  2360.     while( xpos <= 0 )
  2361.        xpos += gl_wchar;
  2362.     PXPOS( PopPtr )   = xpos;
  2363.     
  2364.  
  2365.     /* Handle the Y Coordinate Adjustments...
  2366.      *------------------------------------------------------------------
  2367.      */
  2368.     /* Calculates the Position so that the submenu start item 
  2369.      * is adjacent to the button that activated it.
  2370.      */
  2371.     position = ( PopUpItem( PopPtr ) + ( PNUM( PopPtr ) > PHEIGHT( PopPtr ) )) / PHEIGHT( PopPtr );
  2372.     if( position )
  2373.     {
  2374.        position = PopUpItem( PopPtr );
  2375.        temp = PNUM( PopPtr ) - PHEIGHT( PopPtr ) + 1;
  2376.  
  2377.        if( position  >= temp )
  2378.            position   = temp;
  2379.        position -= 1;    
  2380.     }      
  2381.     position = PopUpItem( PopPtr ) - position;
  2382.     ypos = ypos - ( position * gl_hchar );
  2383.  
  2384.     
  2385.     /* Calculates a new position if the submenu exceeds the
  2386.      * height of the screen.
  2387.      */        
  2388.     temp = ypos + PIX_HEIGHT( PopPtr );
  2389.     if( temp > ( yres - 4 ))
  2390.     {
  2391.        if( Display_Flag )
  2392.           ypos = ypos - ( (PHEIGHT( PopPtr ) - position - 1 )* gl_hchar );        
  2393.        else
  2394.        {
  2395.           do
  2396.           {
  2397.              ypos -= gl_hchar;
  2398.              temp = ypos + PIX_HEIGHT( PopPtr );
  2399.           }while( temp > ( yres - 4 ) );
  2400.        }
  2401.     }
  2402.     
  2403.     while( ypos <= ( gl_hchar + ( gl_hchar/2 ) ))
  2404.        ypos += gl_hchar;
  2405.        
  2406.     PYPOS( PopPtr )   = ypos;
  2407. }
  2408.  
  2409.  
  2410.  
  2411.  
  2412. /* WaitForUpButton()
  2413.  * =====================================================================
  2414.  * Waits for an up button.
  2415.  */
  2416. void
  2417. WaitForUpButton( void )
  2418. {
  2419.   MRETS mk;
  2420.   
  2421.   do
  2422.   {
  2423.      Graf_mkstate( &mk );
  2424.   }while( mk.buttons );
  2425. }
  2426.  
  2427.  
  2428.