/* POPMENU.C *========================================================================== * DESCRIPTION: Generic Popup Routines ( New and Improved ) * VERSION 0.00 Started: February 7, 1991 * VERSION 0.10 March 4, 1991 * version 1.00 April 24,1991 * Recreated damaged files * Removed open workstation calls. * vhandle = graf_handle() * * * INCLUDE: POPMENU.H * Requires: POP_HEAD.H * */ /* INCLUDE FILES *========================================================================== */ #include #include #include #include #include #include "pop_head.h" /* PROTOTYPES *========================================================================== */ void InitPopUpMenus( void ); int GetNewPopMenu( void ); POP_PTR GetMenuPtr( int MenuID ); BOOLEAN InitCmdChar( POP_PTR PopPtr ); void BuildCmdChar( POP_PTR PopPtr ); void BuildKeyCodes( POP_PTR PopPtr, char *text, int num_entry ); void DeletePopUpMenu( int MenuID ); int InsertPopUpMenu( char *text, int num_entries, int height ); long PopUpMenuSelect( int MenuID, int xpos, int ypos, int Item ); BOOLEAN Pop_Blit( long *PopPtr, GRECT *xclip, int flag ); BOOLEAN Build_Objects( POP_PTR curptr ); void SetRootNode( OBJECT *tree, int xpos, int ypos, int Width, int Num ); void SetTextNode( OBJECT *tree, int parent, POP_PTR PopPtr, int obj, int Ypos, int textnum, int Width ); long CalcTextBufferSize( POP_PTR PopPtr ); void BuildText( POP_PTR PopPtr ); int CountBlanksNeeded( char *dindex, char *dtemp, int width, int num ); BOOLEAN Pop_Arrow( POP_PTR PopPtr, int obj ); void UpArrowStatus( POP_PTR PopPtr ); void DownArrowStatus( POP_PTR PopPtr ); void SetArrowClickDelay( long delay ); /* KEYCODE CHECKING */ long CheckForKeyCodes( POP_PTR PopPtr, char *text, int num_entry ); char *CheckForCtrl( char *text ); char *CheckForAlt( char *text ); char *CheckForCapKey( char *text ); char *CheckForCmdKey( char *text ); BOOLEAN CheckForDisable( char *text ); BOOLEAN CheckForMenuCheck( char *text ); char *CheckForSubMenu( char *text ); char *CheckForFuncKey( char *text ); BOOLEAN CheckForDuplicate( char *text ); /* APPEARANCE OF ITEMS */ void SetHeight( int MenuID, int Height ); void SetNumItems( int MenuID, int NumItems ); void CheckItem( int MenuID, int item, BOOLEAN check ); void DisableItem( int MenuID, int item ); void EnableItem( int MenuID, int item ); void SetItemCmd( int MenuID, int item, char cmd ); char GetItemCmd( int MenuID, int item ); void SetSubMenuID( int MenuID, int item, int ID ); int GetSubMenuID( int MenuID, int item ); void SetItemMark( int MenuID, int item, char key ); char GetItemMark( int MenuID, int item ); void SetFuncMark( int MenuID, int item, int num ); int GetFuncMark( int MenuID, int item ); void SetItem( int MenuID, int item, char *text ); char *GetItem( int MenuID, int item ); int GetStartItem( int MenuID ); void SetStartItem( int MenuID, int item ); /* SUBMENU HANDLING */ POP_PTR DoSubMenu( POP_PTR PopPtr, int obj ); BOOLEAN ShowSubMenu( int MenuID, int xpos, int ypos, GRECT *rect ); void HideSubMenu( POP_PTR PopPtr ); long EvntSubMenu( POP_PTR PopPtr ); int FindNum( POP_PTR PopPtr, int obj ); long MenuChoice( void ); /* SUBMENU DELAY */ long GetTimeHz( void ); void SetSubMenuDelay( long ms ); void SetSubDragDelay( long ms ); void AdjustToScreen( POP_PTR PopPtr, int xpos, int ypos, GRECT *rect, BOOLEAN byte_flag, BOOLEAN Display_Flag ); void WaitForUpButton( void ); /* EXTERNALS *========================================================================== * These functions and variables are required and must be * supplied externally. */ int BUT_OR( void ); /* Used to ensure left mouse button */ extern int (*BADDR)( void ); extern int BSTATE; extern int BREAL; /* DEFINES *========================================================================== */ #define INIT_DISPLAY_DELAY 300L /* Initial Submenu Display Delay */ #define INIT_DRAG_DELAY 3000L /* Initial Submenu Drag Delay */ #define INIT_CLICK_DELAY 150L /* Delay for STARTING arrow scroll */ #define STLOW 2 #define STMED 3 #define MAX_MENUS 50 /* Maximum # of Menus allowed...*/ #define UP_ARROW 0x01 #define DOWN_ARROW 0x02 #define RIGHT_ARROW 0x03 /* GLOBAL *========================================================================== */ POP_NODE MenuList[ MAX_MENUS ]; /* Menu structure Storage */ BOOLEAN free_flag[ MAX_MENUS ]; char *KeyText[] = { "HOME", /* Text for menu items using these*/ "HELP", /* as keyboard shortcuts... */ "UNDO", "ESC", "INSERT", "CLR", "DEL", "TAB", "ENTER", "RETURN" }; char CmdText[ 10 ]; /* Temp string storage... */ char TempString[ 255 ]; int xout[57]; /* used for vq_extnd() */ long SUBMENU_DELAY; /* Delay time for submenus to appear ( ms ) */ long SUBDRAG_DELAY; /* Delay time for submenus to go active( ms) */ long CLICK_DELAY; /* Delay time for arrows to start scrolling(ms)*/ long TimeInHz; /* Current Time in 200 Hz */ /* Used by the MenuChoice() call */ int CurObject; /* Current Object clicked on. ( Global ) */ int CurMenu; /* Current Menu clicked on. ( Global ) */ /* FUNCTIONS *========================================================================== */ /* InitPopMenus() * ==================================================================== * Simply clears out the flags in the free_flags structure. */ void InitPopUpMenus( void ) { int i; for( i = 0; i < MAX_MENUS; i++ ) free_flag[ i ] = FALSE; SetSubMenuDelay( INIT_DISPLAY_DELAY ); SetSubDragDelay( INIT_DRAG_DELAY ); SetArrowClickDelay( INIT_CLICK_DELAY ); } /* InsertPopMenu() * ==================================================================== * Returns: Pointer to the POPUP structure. * IN: * XHEIGHT ( in characters ) - actually, number of entries displayed * NUM_ENTRIES - Total # of menu entries. * Pointer to text address... * OUT: # >= 0 Valid Menu ID * # < 0 Error messages. * -1 = Memory Allocation Error. * -2 = No more Menu Slots available. */ int InsertPopUpMenu( char *text, int num_entries, int height ) { POP_PTR PopPtr; int MenuID; MenuID = GetNewPopMenu(); /* No more menu slots available.*/ if( MenuID == -1 ) return( -2 ); PopPtr = GetMenuPtr( MenuID ); PPREV( PopPtr ) = ( POP_PTR )NULL; PMENUID( PopPtr ) = MenuID; PXPOS( PopPtr ) = PYPOS( PopPtr ) = 0; PNUM( PopPtr ) = num_entries; POBJECT( PopPtr ) = ( OBJECT *)NULL; /* Take care of height of menu */ if( height > PNUM( PopPtr ) ) height = PNUM( PopPtr ); PHEIGHT( PopPtr ) = height; PIX_HEIGHT( PopPtr ) = ( PHEIGHT( PopPtr ) * gl_hchar ); PTEXT( PopPtr ) = text; if( !InitCmdChar( PopPtr )) { /* If memory error, clear out the Menu Slot * and return a -1. */ free_flag[ MenuID ] = FALSE; MenuID = -1; } return( MenuID ); } /* GetNewPopMenu() * ==================================================================== * Find an unused slot for a menu. * RETURN( -1 ) if there are no more menu slots available. */ int GetNewPopMenu( void ) { int i; for( i = 0; i < MAX_MENUS; i++ ) { if( !free_flag[ i ] ) { free_flag[i] = TRUE; return( i ); } } return( -1 ); } /* GetMenuPtr() * ==================================================================== * Returns a pointer to the Menu Structure if given a valid menuID */ POP_PTR GetMenuPtr( int MenuID ) { return( ( POP_PTR )&MenuList[ MenuID ] ); } /* InitCmdChar() * ==================================================================== * Initializes the CmdChar memory for a menu structure. * The memory is MALLOCed! So the application MUST call * DeletePopMenu() for each menu created. OR ELSE...! * Need to check for malloc failures. * RETURN: FALSE = ERROR! * TRUE = AOK! * It will be up to the program to inform the user that * no menu was created and initialized. */ BOOLEAN InitCmdChar( POP_PTR PopPtr ) { int i; PCmdChar( PopPtr ) = ( CmdChar *)malloc( (long)( (long)PNUM( PopPtr ) + 10L ) * (long)sizeof( CmdChar )); /* We go up to + 2 because we have ... * PNUM() = UP ARROW TEXT * PNUM+1 = DOWN ARROW TEXT * PNUM+2 = BLANK TEXT */ if( PCmdChar( PopPtr ) ) { for( i = 0; i <= ( PNUM( PopPtr ) + 2 ); i++ ) { CmdState( PopPtr->CharPtr[ i ] ) = NORMAL; CmdSubMenu( PopPtr->CharPtr[ i ] ) = -1; CmdFlag( PopPtr->CharPtr[i] ) = '\0'; } BuildCmdChar( PopPtr ); return( TRUE ); } else return( FALSE ); } /* BuildCmdChar() * ==================================================================== * Calculate the text buffer size that we'll have to malloc, based upon * the largest text string and the number of entries. */ void BuildCmdChar( POP_PTR PopPtr ) { char *txtptr; long length; int count; txtptr = PTEXT( PopPtr ); if( txtptr ) { for( count = 0; count < PNUM( PopPtr ); count++ ) { length = strlen( txtptr ); BuildKeyCodes( PopPtr, txtptr, count ); txtptr += ( length + 1 ); } } } /* BuildKeyCodes() * ===================================================================== * Checks the text string for various keycodes and subtracts * an appropriate amount from the length if any are found. * For many of these, there should only be one of them. * We then ADD back in the amount of ACTUAL text usage this item * would take. IE: a Function Key can be F10 so 3 characters... * or a CmdKey can be the letters: 'DELETE' 6 characters added. * This will also fill up the CMDCHAR structures. */ void BuildKeyCodes( POP_PTR PopPtr, char *text, int num_entry ) { char *txtptr; if( ( txtptr = CheckForCtrl( text )) != NULL ) { CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '^'; CmdHotKey( PopPtr->CharPtr[ num_entry ] ) = *( ++txtptr ); } if( ( txtptr = CheckForAlt( text ) ) != NULL ) { CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '&'; CmdHotKey( PopPtr->CharPtr[ num_entry ] ) = *( ++txtptr ); } if( ( txtptr = CheckForCapKey( text ) ) != NULL ) { CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '|'; CmdHotKey( PopPtr->CharPtr[ num_entry ] ) = *( ++txtptr ); } if( ( txtptr = CheckForCmdKey( text ) ) != NULL ) { /* We ACKNOWLEDGE ----- * [H] HELP [U] Undo [E] Escape [I] Insert * [D] DEL [C] Clr [h] Home [T] TAB * [e] ENTER [R] RETURN */ CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '$'; CmdHotKey( PopPtr->CharPtr[ num_entry ] ) = *( ++txtptr ); } /* Check for the '@' character which will tell us to * disable the current menu item that we are working on. */ if( CheckForDisable( text ) ) DisableItem( PMENUID( PopPtr ), num_entry ); else EnableItem( PMENUID( PopPtr ), num_entry ); /* Check for CheckMark ( ! ) */ CheckItem( PMENUID( PopPtr ), num_entry, CheckForMenuCheck( text ) ); if( ( txtptr = CheckForSubMenu( text ) ) != NULL ) { CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '~'; txtptr++; CmdSubMenu( PopPtr->CharPtr[ num_entry ] ) = atoi( txtptr ); } if( ( txtptr = CheckForFuncKey( text ) ) != NULL ) { CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '#'; txtptr++; CmdFuncKey( PopPtr->CharPtr[ num_entry ] ) = atoi( txtptr ); } if( CheckForDuplicate( text ) ) CmdFlag( PopPtr->CharPtr[ num_entry ] ) = '*'; } /* DeletePopUpMenu() * ==================================================================== */ void DeletePopUpMenu( int MenuID ) { POP_PTR PopPtr; if( free_flag[ MenuID ] ) { free_flag[ MenuID ] = FALSE; PopPtr = GetMenuPtr( MenuID ); if( PCmdChar( PopPtr ) ) free( PCmdChar( PopPtr ) ); PCmdChar( PopPtr ) = ( CmdChar *)NULL; } } /* PopUpMenuSelect() * ==================================================================== * Displays the actual popup menu. * Let's the user select an object. * IN: POP_PTR PopPtr; * int xpos; * int ypos; * int Start_Obj; Index to show selected else start with 0 * BOOLEAN Arrow_Flag; If TRUE, display arrows if height < num_items. * FALSE, don't display arrows if height < num_items. * returns: long where.... * HIGH WORD LOW WORD * MenuID Item Selected. * -1L if NONE selected. * If the MenuID is valid, but the item is -1, then * the user clicked on a disabled menu item. * -2L if Memory Allocation Error * -3L if workstation error. */ long PopUpMenuSelect( int MenuID, int xpos, int ypos, int Item ) { long obj; POP_PTR PopPtr; GRECT rect; MRETS mk; int max_height; WaitForUpButton(); wind_update( BEG_MCTRL ); vq_extnd( vhandle, 0, xout ); xres = xout[0]; yres = xout[1]; vex_butv( vhandle, BUT_OR, &BADDR ); PopPtr = GetMenuPtr( MenuID ); PopUpItem( PopPtr ) = Item; PXPOS( PopPtr ) = xpos; PYPOS( PopPtr ) = ypos; PPREV( PopPtr ) = ( POP_PTR )NULL; /* Take care of height of menu */ if( PHEIGHT( PopPtr ) > PNUM( PopPtr ) ) PHEIGHT( PopPtr ) = PNUM( PopPtr ); /* Limit Height to Height of screen - ( 2 * gl_hchars ) */ max_height = yres - ( 2 * gl_hchar ); while( ( PHEIGHT( PopPtr ) * gl_hchar ) > max_height ) PHEIGHT( PopPtr ) -= 1; PIX_HEIGHT( PopPtr ) = ( PHEIGHT( PopPtr ) * gl_hchar ); /* Take care of the width of the menu here...*/ PTEXTBUFFSIZE( PopPtr ) = CalcTextBufferSize( PopPtr ); PIX_WIDTH( PopPtr ) = ( PWIDTH( PopPtr ) * gl_wchar ); AdjustToScreen( PopPtr, xpos, ypos, &rect, FALSE, TRUE ); if( Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 0 ) ) { if( Build_Objects( PopPtr ) ) { obj = EvntSubMenu( PopPtr ); if( obj == -2L ) /* returning from a submenu - error */ obj = -1L; /* then set it to click on nothing */ Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 1 ); } else /* Memory Allocation Error in Space Capsule */ obj = -2L; if( POBJECT( PopPtr ) ) free( POBJECT( PopPtr ) ); if( PTEXTBUFF( PopPtr ) ) free( PTEXTBUFF( PopPtr ) ); } else /* ERROR: Memory Allocation Error */ obj = -2L; POBJECT( PopPtr ) = ( OBJECT *)NULL; PTEXTBUFF( PopPtr ) = ( char *)NULL; Evnt_button( 1, 1, 0, &mk ); vex_butv( vhandle, BADDR, &BADDR ); wind_update( END_MCTRL ); return( obj ); } /* Build_Objects() * ==================================================================== * RETURN: TRUE - AOK * FALSE- Memory Allocation Error */ BOOLEAN Build_Objects( POP_PTR PopPtr ) { int num_objects; GRECT rect; int text_index; /* Index into the text data 0 based */ int count; int ypos; /* Y position of object ( in characters*/ int temp; OBJECT *tree; int obj; /* Calculate the number of objects needed... * ROOT + # of STRINGS + ( safety buffer ) */ num_objects = 1 + PHEIGHT( PopPtr ) + 10; /* + 10 for safety buffer */ /* get the memory for this number of objects */ POBJECT( PopPtr ) = ( OBJECT * )malloc( (long)(sizeof( OBJECT ) * (long)num_objects )); /* Malloc the text memory and then build them. */ /* Need malloc checking of course... */ PTEXTBUFF( PopPtr) = malloc( PTEXTBUFFSIZE( PopPtr ) ); /* Memory Allocation Error */ if( !POBJECT( PopPtr ) || !PTEXTBUFF( PopPtr ) ) return( FALSE ); BuildText( PopPtr ); /* setup the root */ tree = POBJECT( PopPtr ); SetRootNode( tree, PXPOS( PopPtr ), PYPOS( PopPtr ), PWIDTH( PopPtr ), PHEIGHT( PopPtr ) ); ypos = text_index = 0; obj = 1; /* Start Creating from object 1 */ PFIRST( PopPtr ) = 1; /* first G_STRING starts at 3 */ POFFSET( PopPtr ) = ( PopUpItem( PopPtr ) + ( PNUM( PopPtr ) > PHEIGHT( PopPtr ) )) / PHEIGHT( PopPtr ); if( POFFSET( PopPtr ) ) { POFFSET( PopPtr ) = PopUpItem( PopPtr ); temp = PNUM( PopPtr ) - PHEIGHT( PopPtr ) + 1; if( POFFSET( PopPtr ) >= temp ) POFFSET( PopPtr ) = temp; } text_index = POFFSET( PopPtr ); for( count = 0; count < PHEIGHT( PopPtr ); count++ ) { if( count == 0 ) { if( POFFSET( PopPtr ) ) SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, PNUM( PopPtr ), PWIDTH( PopPtr ) ); else { if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED ) CheckObj( obj ); SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) ); } } else { if( count >= ( PHEIGHT( PopPtr ) - 1 ) ) { if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr ) ) < PNUM( PopPtr ) ) SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, PNUM( PopPtr ) + 1, PWIDTH( PopPtr ) ); else { if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED ) CheckObj( obj ); SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) ); } } else { if( CmdState( PopPtr->CharPtr[ text_index ] ) & CHECKED ) CheckObj( obj ); SetTextNode( tree, ROOT, PopPtr, obj++, ypos++, text_index++, PWIDTH( PopPtr ) ); } } } PLAST( PopPtr ) = obj - 1; ObFlags( obj - 1 ) = LASTOB; rect = ObRect( ROOT ); rect.g_x -= 2; rect.g_y -= 2; rect.g_w += 5; rect.g_h += 5; rc_intersect( &desk, &rect ); /* clip to desktop */ Objc_draw( tree, ROOT, MAX_DEPTH, &rect ); return( TRUE ); } /* SetRootNode() * ==================================================================== */ void SetRootNode( OBJECT *tree, int xpos, int ypos, int Width, int Num ) { ObNext( ROOT ) = -1; ObHead( ROOT ) = -1; ObTail( ROOT ) = -1; ObType( ROOT ) = G_BOX; ObFlags( ROOT ) = NONE; ObState( ROOT ) = SHADOWED; ObIndex( ROOT ) = 0xFF1100L; /* fix up for screen...*/ ObX( ROOT ) = xpos; ObY( ROOT ) = ypos; ObW( ROOT ) = Width * gl_wchar; ObH( ROOT ) = Num * gl_hchar; } /* SetTextNode() * ==================================================================== */ void SetTextNode( OBJECT *tree, int parent, POP_PTR PopPtr, int obj, int Ypos, int textnum, int Width ) { ObNext( obj ) = -1; ObHead( obj ) = -1; ObTail( obj ) = -1; ObType( obj ) = G_STRING; ObFlags( obj ) = NONE; ObString( obj ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * textnum ]; ObState( obj ) = CmdState( PopPtr->CharPtr[ textnum ] ); ObX( obj ) = 0; ObY( obj ) = Ypos * gl_hchar; ObW( obj ) = Width * gl_wchar; ObH( obj ) = gl_hchar; objc_add( tree, parent, obj ); } /* CalcTextBufferSize() * ==================================================================== * Calculate the text buffer size that we'll have to malloc, based upon * the largest text string and the number of entries. */ long CalcTextBufferSize( POP_PTR PopPtr ) { char *xptr; long size; long xsize; long actual; int i; size = 0L; PWIDTH( PopPtr ) = 0; xptr = PTEXT( PopPtr ); if( xptr ) { for( i = 0; i < PNUM( PopPtr ); i++ ) { xsize = strlen( xptr ); actual = CheckForKeyCodes( PopPtr, xptr, i ); if( actual > size ) size = actual; xptr += ( xsize + 1 ); } /* size-> now contains the length of the longest string. */ size += 1; /* for a blank after...*/ size += 2; /* for 2 blanks before the string*/ /* Number of characters in our longest string...*/ PWIDTH( PopPtr ) = (int)size; size += 1; /* for the null. */ size += 10; /* for the Control Keys or RT Arrow */ size *= PNUM( PopPtr ); /* For the number of entries. */ size += ( size / 2 ); /* Buffer area, just in case */ } return( size ); } /* BuildText() * ==================================================================== * Takes the pointer to the menu_text given by the program and * builds the text in the memory buffer where... * 1) 2 spaces before each text string. * 2) 1 space after the longest string. * 3) pads spaces after shorter strings to match the longest string. * 4) Null terminates each string. * 5) *# - The char following is duplicated the width of the menu. * Characters following it are ignored to the end of the string. * example: *- The line is filled with "---------" * 6) @ - means to disable this menu item * 7) /# - means that there is a keycode: ie: /T == [T] * 8) ## - means that there is a function key. #10 is F10 * 9) ^# - means that there is a ctrl key: ^H * 10) $# - means that there is a CmdKey: ie: $H = HELP ( 4 chars ) * We ACKNOWLEDGE ----- * [H] HELP [U] Undo [E] Escape [I] Insert * [D] DEL [C] Clr [h] Home [T] TAB * [e] ENTER [R] RETURN * * 11) &# - means that there is an ALT Key: &A - Alt A * 12) ~# - means that there is a submenu: ~1 - attach submenu 1 * 13) ! - means that the menu item is CHECKED. */ void BuildText( POP_PTR PopPtr ) { char *sindex; /* Pointer to source text... */ char *stemp; /* Pointer INTO source text */ char *dindex; /* Pointer to destination text */ char *dtemp; /* Pointer INTO destination text*/ int num_entries; /* Number of entries counter... */ int num; int num2; long length; /* length of a string... */ long i; /* temp variable... */ if( !PTEXTBUFF( PopPtr ) ) /* if there is no destination buffer...*/ return; /* skip doing all of this... */ sindex = PTEXT( PopPtr ); dindex = PTEXTBUFF( PopPtr ); for( num_entries = 0; num_entries <= PNUM( PopPtr ); num_entries++ ) { if( num_entries < PNUM( PopPtr ) ) { if( ( stemp = strchr( sindex, '*' )) != NULL ) { /* Check for a '*' character. The character following * will be extened the length of the string. * All characters following after that will be ignored. */ stemp++; /* gets us to the item we want to duplicate */ dtemp = dindex; /* get a ptr to the destination */ /* Duplicate the character to the proper width wanted...*/ for( i = 0; i < PWIDTH( PopPtr ); i++ ) *dtemp++ = *stemp; *dtemp = '\0'; /* NULL terminate the string */ } else { strcpy( dindex, " " ); /* Pad the entry with 2 blanks */ stemp = sindex; /* Get a pointer to the source */ dtemp = dindex+2; /* Get a pointer to the dest...*/ /* Copy the source string to the destination buffer * skipping the HOT chars that will allow submenus, * disabling menus, keycodes etc. */ while( *stemp ) { switch( *stemp ) { case '@': /* Disable menu item */ case '!': /* CheckMark menu item */ stemp++; break; case '^': /* Ctrl Key */ case '&': /* Alt Key */ case '|': /* Cap Key */ case '$': /* CmdKey */ case '~': /* Submenu */ stemp += 2; break; case '#': /* FuncKey */ stemp += 2; num = CmdFuncKey( PopPtr->CharPtr[ num_entries ] ); if( num > 9 ) stemp++; break; default: *dtemp++ = *stemp++; break; } } /* Now add in the necessary text for the Keycodes..*/ switch( CmdFlag( PopPtr->CharPtr[ num_entries ] ) ) { case '^': /* Ctrl Key */ case '&': /* Alt Key */ num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), 2 ); for( i = 0; i < num; i++ ) *dtemp++ = ' '; /* add a space */ *dtemp++ = (( CmdFlag( PopPtr->CharPtr[ num_entries ] ) == '^' ) ? ( '^' ) : ( 0x07 ) ); *dtemp++ = CmdHotKey( PopPtr->CharPtr[ num_entries ] ); break; case '|': /* Cap Key */ num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), 3 ); for( i = 0; i < num; i++ ) *dtemp++ = ' '; /* add a space */ *dtemp++ = '['; *dtemp++ = CmdHotKey( PopPtr->CharPtr[ num_entries ] ); *dtemp++ = ']'; break; case '$': /* CmdKey */ switch( CmdHotKey( PopPtr->CharPtr[ num_entries ] ) ) { case 'h': /* Home */ strcpy( &CmdText[0], KeyText[0] ); break; case 'H': /* HELP */ strcpy( &CmdText[0], KeyText[1] ); break; case 'U': /* Undo */ strcpy( &CmdText[0], KeyText[2] ); break; case 'E': /* Escape*/ strcpy( &CmdText[0], KeyText[3] ); break; case 'I': /* Insert */ strcpy( &CmdText[0], KeyText[4] ); break; case 'C': /* Clr */ strcpy( &CmdText[0], KeyText[5] ); break; case 'D': /* Delete */ strcpy( &CmdText[0], KeyText[6] ); break; case 'T': /* TAB */ strcpy( &CmdText[0], KeyText[7] ); break; case 'e': /* ENTER */ strcpy( &CmdText[0], KeyText[8] ); break; case 'R': /* RETURN */ strcpy( &CmdText[0], KeyText[9] ); break; default: CmdText[0] = '\0'; break; } num = (int)strlen( &CmdText[0] ); num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), num ); for( i = 0; i < num; i++ ) *dtemp++ = ' '; /* add a space */ *dtemp = '\0'; strcat( dindex, &CmdText[0] ); length = strlen( dindex ); dtemp = dindex + length; break; case '#': /* FuncKey */ num = CmdFuncKey( PopPtr->CharPtr[ num_entries ] ); num = (( num < 10 ) ? ( 2 ) : ( 3 ) ); num2 = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), num ); for( i = 0; i < num2; i++ ) *dtemp++ = ' '; /* add a space */ *dtemp++ = 'F'; num = CmdFuncKey( PopPtr->CharPtr[ num_entries ] ); if( num < 10 ) *dtemp++ = num + '0'; else /* handle special case for F10 */ { *dtemp++ = '1'; *dtemp++ = '0'; } break; case '~': /* Submenu */ num = CountBlanksNeeded( dindex, dtemp, PWIDTH( PopPtr ), 2 ); for( i = 0; i < num; i++ ) *dtemp++ = ' '; /* add a space */ *dtemp++ = RIGHT_ARROW; /* Right Arrow */ *dtemp++ = ' '; break; default: break; } *dtemp = '\0'; /* Get the length of the destination string and * pad it with blanks to make it the width of * the final string wanted. */ length = strlen( dindex ); for( i = length; i < PWIDTH( PopPtr ); i++ ) strcat( dindex, " " ); } /* Get the length of the actual string and * advance it to the next string skipping the NULL * and moving to the first char of the next string. */ length = strlen( sindex ); sindex = sindex + length + 1; /* Advance dest to end of the new string */ dindex = dindex + PWIDTH( PopPtr ) + 1; } else { /* The PNUM() menu item will have an UP Arrow in its text */ strcpy( dindex, " " ); for( i = 1; i < PWIDTH( PopPtr ); i++ ) strcat( dindex, " " ); *(dindex + 2 ) = UP_ARROW; /* UP ARROW */ /* The PNUM() + 1 menu item will have a DOWN Arrow in its text */ dindex = dindex + PWIDTH( PopPtr ) + 1; strcpy( dindex, " " ); for( i = 1; i < PWIDTH( PopPtr ); i++ ) strcat( dindex, " " ); *(dindex + 2 ) = DOWN_ARROW; /* DOWN ARROW */ /* The PNUM() + 2 menu item will have a blank string in its text*/ dindex = dindex + PWIDTH( PopPtr ) + 1; strcpy( dindex, " " ); for( i = 1; i < PWIDTH( PopPtr ); i++ ) strcat( dindex, " " ); } } } /* CountBlanksNeeded() * ==================================================================== * Counts the number of blanks to go between the menu item and the * control key or whatever. */ int CountBlanksNeeded( char *dindex, char *dtemp, int width, int num ) { long length; *dtemp = '\0'; length = strlen( dindex ); return( width - 1 - (int)length - num ); } /* Pop_Arrow() *========================================================================== * Scroll the popup menu items if up or down arrow is selected. * * IN: int obj: up or down arrow is selected. * int *offset: Offset into the menu items text array * int num_items: Total number of menu items involved. * char *items[]: Pointer to the text array * * OUT: TRUE - ended on a submenu * FALSE - ended on a normal menu item. */ BOOLEAN Pop_Arrow( POP_PTR PopPtr, int obj ) { OBJECT *tree; int DrawFlag; int i,j; GRECT rect; GRECT xrect, clip; MRETS mk; int pxy[8]; int xclip[4]; long location = 0L; /* SCREEN MFDB */ BOOLEAN DelayFlag; BOOLEAN ReturnFlag; tree = POBJECT( PopPtr ); xrect = ObRect( obj ); objc_offset( tree, obj, &xrect.g_x, &xrect.g_y ); DelayFlag = TRUE; ReturnFlag = FALSE; do { BSTATE = 0; DrawFlag = FALSE; /* Up Arrow Selected AND not at the top */ if( ( obj == PFIRST( PopPtr ) ) && ( POFFSET( PopPtr ) > 0 )) { POFFSET( PopPtr ) -= 1; DrawFlag = TRUE; /* special handling...to make it jump to zero if * we are displaying item 2, we can jump to zero, blit and * redraw the top 2 items instead. */ if( POFFSET( PopPtr ) == 1 ) POFFSET( PopPtr ) = 0; } /* DOWN Arrow and not within PHEIGHT() items from the bottom */ if( ( obj == PLAST( PopPtr ) ) && ( POFFSET( PopPtr ) <= ( PNUM( PopPtr ) - PHEIGHT( PopPtr ) ) ) ) { POFFSET( PopPtr ) += 1; DrawFlag = TRUE; /* Special handling for first item only */ if( POFFSET( PopPtr ) == 1 ) { if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr ) - 1 ) < PNUM( PopPtr ) ) POFFSET( PopPtr ) += 1; } } j = POFFSET( PopPtr ); if( DrawFlag ) { UpArrowStatus( PopPtr ); DownArrowStatus( PopPtr ); /* Get the Blit Rectangle */ clip = ObRect( PFIRST( PopPtr )); objc_offset( tree, ROOT, &clip.g_x, &clip.g_y ); clip.g_h *= PHEIGHT( PopPtr ); /* Calculate the height */ if( strchr( ObString( PFIRST( PopPtr ) ), UP_ARROW ) ) { clip.g_h -= gl_hchar; /* Subtract 1 char height */ clip.g_y += gl_hchar; /* Move the Ypos down 1 level */ } if( strchr( ObString( PLAST( PopPtr )), DOWN_ARROW ) ) { clip.g_h -= gl_hchar; /* Subtract 1 char height */ } /* Set up SOURCE rectangle */ rc_intersect( &desk, &clip ); rect = clip; rc_2xy( &clip, ( WORD *)&pxy[0] ); if( obj == PFIRST( PopPtr ) ) clip.g_y += gl_hchar; if( obj == PLAST( PopPtr ) ) clip.g_y -= gl_hchar; rc_intersect( &desk, &clip ); rc_2xy( &clip, ( WORD *)&pxy[4] ); clip = rect; rc_2xy( &clip, ( WORD *)&xclip[0] ); vs_clip( vhandle, 1, xclip ); graf_mouse( M_OFF, 0L ); vro_cpyfm( vhandle, 3, pxy, ( MFDB *)&location, ( MFDB *)&location ); graf_mouse( M_ON, 0L ); for( i = PFIRST( PopPtr ); i <= PLAST( PopPtr ); i++ ) { if( ObType( i ) == G_STRING ) { if( i == PFIRST( PopPtr ) && strchr( ObString( PFIRST( PopPtr )), UP_ARROW )) { ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * PNUM( PopPtr ) ]; continue; } if( i == PLAST( PopPtr ) && strchr( ObString( PLAST( PopPtr )), DOWN_ARROW )) { ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 1 ) ]; continue; } SetNormal( i ); if( CmdState( PopPtr->CharPtr[j] ) & CHECKED ) CheckObj( i ); if( CmdState( PopPtr->CharPtr[j] ) & DISABLED ) Disable( i ); ObString( i ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * j++ ]; } } if( obj == PFIRST( PopPtr ) ) { rect = ObRect( obj ); objc_offset( tree, obj, &rect.g_x, &rect.g_y ); if( strchr( ObString( obj ), UP_ARROW ) ) rect.g_y += gl_hchar; else { rect.g_h += gl_hchar; ObString( obj ) = &PTEXTBUFF( PopPtr )[ 0 ]; if( !(CmdState( PopPtr->CharPtr[ 0 ] ) & DISABLED) ) Select( obj ); if( CmdState( PopPtr->CharPtr[ 0 ] ) & CHECKED ) CheckObj( obj ); } } if( obj == PLAST( PopPtr ) ) { rect = ObRect( obj ); objc_offset( tree, obj, &rect.g_x, &rect.g_y ); rect.g_y -= gl_hchar; if( !strchr( ObString( obj ), DOWN_ARROW ) ) { rect.g_h += gl_hchar; ObString( obj ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) - 1 )]; if( !(CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & DISABLED) ) Select( obj ); if( CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & CHECKED ) CheckObj( obj ); } } Objc_draw( tree, ROOT, MAX_DEPTH, &rect ); } else { if( ( obj == PFIRST( PopPtr ) ) && ( !strchr( ObString( obj ), UP_ARROW )) && ( !( CmdState( PopPtr->CharPtr[ 0 ] ) & DISABLED )) && ( CmdFlag( PopPtr->CharPtr[0] ) == '~' ) ) ReturnFlag = TRUE; if( ( obj == PLAST( PopPtr ) ) && ( !strchr( ObString( obj ), DOWN_ARROW ) ) && ( !( CmdState( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) & DISABLED )) && ( CmdFlag( PopPtr->CharPtr[ PNUM( PopPtr ) - 1 ] ) == '~' ) ) ReturnFlag = TRUE; } if( DelayFlag ) { Evnt_timer( CLICK_DELAY ); DelayFlag = FALSE; } Graf_mkstate( &mk ); }while( (( BSTATE == 1 ) || ( BREAL == 1 )) && ( xy_inrect( mk.x, mk.y, &xrect ))); Evnt_button( 1, 1, 0, &mk ); /* Make sure we have an up button...*/ return( ReturnFlag ); } /* Pop_Blit() * ==================================================================== * Blit from screen to buffer or buffer to screen for Popup Box redraws. * * IN: long *PopPtr: Pointer to memory buffer... * GRECT *clip: GRECT of clip blit area * int flag: 0 - blit from screen to buffer * 1 - blit from buffer to screen * * OUT: returns TRUE if successful, FALSE if failed. */ BOOLEAN Pop_Blit( long *PopPtr, GRECT *xclip, int flag ) { long location = 0L; /* SCREEN MFDB */ int nplanes; /* Number of planes */ unsigned long size; /* size of malloc */ int pxy[8]; /* pxy for blit */ GRECT clip; MFDB PopMFDB; /* buffer MFDB */ clip = ( GRECT )*xclip; clip.g_x -= 2; /* adjust the clip area to take care of*/ clip.g_y -= 2; /* the edges and shadows... */ clip.g_w += 5; clip.g_h += 5; rc_2xy( &clip, ( WORD *)&pxy[0] ); vs_clip( vhandle, 1, pxy ); vq_extnd( vhandle, 1, work_out ); nplanes = work_out[4]; if( !flag ) /* screen to buffer blit*/ { size = (unsigned long)(((long)clip.g_w + 7L )/8L) * (long)clip.g_h * (long)nplanes; size *= 2L; *PopPtr = (long )malloc( (unsigned long ) size ); } if( !*PopPtr ) return( FALSE ); PopMFDB.fd_addr = (long *)*PopPtr; /* Setup the MFDB */ PopMFDB.fd_w = clip.g_w; PopMFDB.fd_h = clip.g_h; PopMFDB.fd_wdwidth = ( clip.g_w + 15 ) / 16; PopMFDB.fd_stand = 0; PopMFDB.fd_nplanes = nplanes; PopMFDB.fd_r1 = PopMFDB.fd_r2 = PopMFDB.fd_r3 = 0; graf_mouse( M_OFF, 0L ); if(!flag ) { /* Screen to buffer blit */ rc_intersect( &desk, &clip ); rc_2xy( &clip, ( WORD *)&pxy[0] ); pxy[4] = pxy[5] = 0; pxy[6] = clip.g_w - 1; pxy[7] = clip.g_h - 1; vro_cpyfm( vhandle, 3, pxy, ( MFDB *)&location, &PopMFDB ); } else { /* Buffer to screen blit */ rc_intersect( &desk, &clip ); pxy[0] = pxy[1] = 0; pxy[2] = clip.g_w - 1; pxy[3] = clip.g_h - 1; rc_2xy( &clip, ( WORD *)&pxy[4] ); vro_cpyfm( vhandle, 3, pxy, &PopMFDB, ( MFDB *)&location ); if( *PopPtr ) free( (long *)*PopPtr ); *PopPtr = 0L; } graf_mouse( M_ON, 0L ); return( TRUE ); } /* UpArrowStatus() * ===================================================================== */ void UpArrowStatus( POP_PTR PopPtr ) { OBJECT *tree; GRECT rect; tree = POBJECT( PopPtr ); /* Check if we need to activate the UP ARROW or deactivate it */ if( POFFSET( PopPtr ) ) /* display the UP ARROW */ { if( !strchr( ObString( PFIRST( PopPtr )), UP_ARROW ) ) { rect = ObRect( PFIRST( PopPtr ) ); objc_offset( tree, PFIRST( PopPtr ), &rect.g_x, &rect.g_y ); SetNormal( PFIRST( PopPtr ) ); ObString( PFIRST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * PNUM( PopPtr ) ]; Objc_draw( tree, ROOT, MAX_DEPTH, &rect ); } } else ObString( PFIRST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 2 ) ]; } /* DownArrowStatus() * ===================================================================== */ void DownArrowStatus( POP_PTR PopPtr ) { OBJECT *tree; GRECT rect; tree = POBJECT( PopPtr ); /* Check if we need to display the DOWN ARROW */ /* DISPLAY the DOWN ARROW */ if( ( POFFSET( PopPtr ) + PHEIGHT( PopPtr )) <= PNUM( PopPtr ) ) { if( !strchr( ObString( PLAST( PopPtr )), DOWN_ARROW ) ) { rect = ObRect( PLAST( PopPtr ) ); objc_offset( tree, PLAST( PopPtr ), &rect.g_x, &rect.g_y ); SetNormal( PLAST( PopPtr ) ); ObString( PLAST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 1 ) ]; Objc_draw( tree, ROOT, MAX_DEPTH, &rect ); } } else ObString( PLAST( PopPtr ) ) = &PTEXTBUFF( PopPtr )[ ( PWIDTH( PopPtr ) + 1 ) * ( PNUM( PopPtr ) + 2 ) ]; } /* SetArrowClickDelay() * ===================================================================== * Set the Click Delay before the menu starts to scroll. */ void SetArrowClickDelay( long delay ) { CLICK_DELAY = (( delay > 0L ) ? ( delay ) : ( INIT_CLICK_DELAY )); } /* CHECK FOR KEYCODES... * ===================================================================== * Checks the string for the keystrings below so that they may be * subtracted from the length of the string for calculation purposes. */ /* CheckForCtrl() * ===================================================================== * Looks for a ^ in the string. * Returns a pointer to the string where it starts. */ char *CheckForCtrl( char *text ) { return( strchr( text, '^' ) ); } /* CheckForAlt() * ===================================================================== * Looks for a & in the string. * Returns a pointer to the string where it starts. */ char *CheckForAlt( char *text ) { return( strchr( text, '&' ) ); } /* CheckForCapKey() * ===================================================================== * Looks for a / in the string. * Returns a pointer to the string where it starts. */ char *CheckForCapKey( char *text ) { return( strchr( text, '|' ) ); } /* CheckForCmdKey() * ===================================================================== * Looks for a $ in the string. * Returns pointer to the string where it starts. */ char *CheckForCmdKey( char *text ) { return( strchr( text, '$' ) ); } /* CheckForDisable() * ===================================================================== * Looks for a @ in the string. * Returns TRUE if found, FALSE if not. */ BOOLEAN CheckForDisable( char *text ) { return( ( BOOLEAN )strchr( text, '@' ) ); } /* CheckForMenuCheck() * ===================================================================== * Looks for a ! in the string. * Returns TRUE if found, FALSE if not. */ BOOLEAN CheckForMenuCheck( char *text ) { return( ( BOOLEAN )strchr( text, '!' ) ); } /* CheckForSubMenu() * ===================================================================== * Looks for a ~ in the string. * Returns the pointer to the string where it starts. */ char *CheckForSubMenu( char *text ) { return( strchr( text, '~' ) ); } /* CheckForFuncKey() * ===================================================================== * Looks for a # in the string. * Returns the pointer to the string if found. */ char *CheckForFuncKey( char *text ) { return( strchr( text, '#' ) ); } /* CheckForDuplicate() * ===================================================================== * Looks for a * in the string. * Returns TRUE if found, FALSE if not. * The length of the string to compare should be ZERO. :-) * This is because this string will be the width of the menu. */ BOOLEAN CheckForDuplicate( char *text ) { return( ( BOOLEAN )strchr( text, '*' )); } /* CheckForKeyCodes() * ===================================================================== * Checks the text string for various keycodes and subtracts * an appropriate amount from the length if any are found. * For many of these, there should only be one of them. * We then ADD back in the amount of ACTUAL text usage this item * would take. IE: a Function Key can be F10 so 3 characters... * or a CmdKey can be the letters: 'DEL' 3 characters added. * This will also fill up the CMDCHAR structures. */ long CheckForKeyCodes( POP_PTR PopPtr, char *text, int num_entry ) { long length; long num; length = strlen( text ); if( strchr( text, '@' )) length -= 1L; if( strchr( text, '!' )) length -= 1L; switch( CmdFlag( PopPtr->CharPtr[ num_entry ] ) ) { case '^': case '~': case '#': case '&': length += 1L; break; case '|': length += 2L; break; case '$': switch( CmdHotKey( PopPtr->CharPtr[ num_entry ] ) ) { case 'h': /* Home */ strcpy( &CmdText[0], KeyText[0] ); break; case 'H': /* HELP */ strcpy( &CmdText[0], KeyText[1] ); break; case 'U': /* Undo */ strcpy( &CmdText[0], KeyText[2] ); break; case 'E': /* Escape*/ strcpy( &CmdText[0], KeyText[3] ); break; case 'I': /* Insert */ strcpy( &CmdText[0], KeyText[4] ); break; case 'C': /* Clr */ strcpy( &CmdText[0], KeyText[5] ); break; case 'D': /* Delete */ strcpy( &CmdText[0], KeyText[6] ); break; case 'T': /* TAB */ strcpy( &CmdText[0], KeyText[7] ); break; case 'e': /* ENTER */ strcpy( &CmdText[0], KeyText[8] ); break; case 'R': /* RETURN */ strcpy( &CmdText[0], KeyText[9] ); break; default: CmdText[0] = '\0'; break; } num = strlen( &CmdText[0] ); num = num - 2 + 1; /* minus the ctrl codes + 1 space */ length += num; break; case '*': length = 1L; break; default: break; } return( length ); } /* END OF KEYCODE CHECKING * ===================================================================== */ /* CONTROLLING THE APPEARANCE OF ITEMS * ===================================================================== */ /* SetHeight() * ===================================================================== * Set the height of the Menu.( in chars ). */ void SetHeight( int MenuID, int Height ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); if( Height > 0 ) PHEIGHT( PopPtr ) = Height; } /* SetNumItems() * ===================================================================== * Set the number of items in the menu. This can be used to display * fewer items than the InsertMenu() call created. */ void SetNumItems( int MenuID, int NumItems ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); if( NumItems > 0 ) PNUM( PopPtr ) = NumItems; } /* CheckItem() * ===================================================================== * Places or removes a check mark at the left of a menu item. */ void CheckItem( int MenuID, int item, BOOLEAN check ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); if( check ) CmdState( PopPtr->CharPtr[ item ] ) |= CHECKED; else CmdState( PopPtr->CharPtr[ item ] ) &= ~CHECKED; } /* DisableItem() * ===================================================================== * Disables a menu item. */ void DisableItem( int MenuID, int item ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); CmdState( PopPtr->CharPtr[ item ] ) |= DISABLED; } /* EnableItem() * ===================================================================== * Enables a menu item. */ void EnableItem( int MenuID, int item ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); CmdState( PopPtr->CharPtr[ item ] ) &= ~DISABLED; } /* SetItemCmd() * ===================================================================== * This can be used to attach a submenu to a menu item. * You must still call 'SetSubMenuID()' tell the popups which * submenu to attach. Otherwise, this is a great way to change * the keyboard shortcuts. Use SetItemMark() to change the * keyboard shortcuts AFTER setting the type with the SetItemCmd(). */ void SetItemCmd( int MenuID, int item, char cmd ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); CmdFlag( PopPtr->CharPtr[ item ] ) = cmd; } /* GetItemCmd() * ===================================================================== * This can be used to determine if a submenu is attached to * this menu item. It returns the current command. * RETURNS: * '~' == SubMenu for example */ char GetItemCmd( int MenuID, int item ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); return( CmdFlag( PopPtr->CharPtr[ item ] ) ); } /* SetSubMenuID() * ===================================================================== * This can be used to change the ID of the submenu associated * with a menu item. Nothing happens of course, if the menu_item * does not have a submenu CMD. */ void SetSubMenuID( int MenuID, int item, int ID ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); if( GetItemCmd( MenuID, item ) == '~' ) CmdSubMenu( PopPtr->CharPtr[ item ] ) = ID; } /* GetSubMenuID() * ===================================================================== * This can be used to get the ID of the submenu associated * with the menu item. Course, the item must HAVE a submenu in * the first place. Use GetItemCmd to determine if there is a * submenu. * RETURN: -1 if there is no submenu. */ int GetSubMenuID( int MenuID, int item ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); if( GetItemCmd( MenuID, item ) == '~' ) return( CmdSubMenu( PopPtr->CharPtr[ item ] )); else return( -1 ); } /* SetItemMark() * ===================================================================== * This can be used to change the CMDKEY of a keyboard shortcut. * The command type itself is set with SetItem(). All except function * keys and submenus. */ void SetItemMark( int MenuID, int item, char key ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); CmdHotKey( PopPtr->CharPtr[ item ] ) = key; } /* GetItemMark() * ===================================================================== * This can be used to see what the CMDKEY shortcut is associated with * the menu item. All except function keys and submenus. */ char GetItemMark( int MenuID, int item ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); return( CmdHotKey( PopPtr->CharPtr[ item ] ) ); } /* SetFuncMark() * ===================================================================== * This can be used to set the Functio key # that is to be * associated with the menu item. */ void SetFuncMark( int MenuID, int item, int num ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); CmdFuncKey( PopPtr->CharPtr[ item ] ) = num; } /* GetFuncMark() * ===================================================================== * This can be used to get wot function key # is associated with * the menu item. */ int GetFuncMark( int MenuID, int item ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); return( CmdFuncKey( PopPtr->CharPtr[ item ] ) ); } /* SetItem() * ===================================================================== * Set the Menu item string. * 1) The string must not exceed the original string length. * 2) Meta characters will be printed as regular text. * 3) If the string is longer than the original, then * the new string will be truncated. */ void SetItem( int MenuID, int item, char *text ) { POP_PTR PopPtr; int count; char *txtptr; long length; long length2; PopPtr = GetMenuPtr( MenuID ); txtptr = PTEXT( PopPtr ); if( txtptr ) { for( count = 0; count < item; count++ ) { length = strlen( txtptr ); txtptr += ( length + 1 ); } /* we've reached the text area that we want. */ length = strlen( txtptr ); strncpy( txtptr, text, length ); length2 = strlen( txtptr ); for( count = (int)length2; count < length; count++ ) strcat( txtptr, " " ); } } /* GetItem() * ===================================================================== * Returns the string associated with the menu item WITHOUT * the meta characters. */ char *GetItem( int MenuID, int item ) { POP_PTR PopPtr; int count; char *txtptr; char *sptr; char *dptr; long length; PopPtr = GetMenuPtr( MenuID ); txtptr = PTEXT( PopPtr ); if( txtptr ) { for( count = 0; count < item; count++ ) { length = strlen( txtptr ); txtptr += ( length + 1 ); } /* we've reached the text area that we want. */ length = strlen( txtptr ); sptr = txtptr; dptr = &TempString[0]; while( *sptr ) { switch( *sptr ) { case '*': case '@': case '!': sptr++; break; case '^': case '~': case '&': case '$': case '|': sptr += 2; break; case '#': sptr += 2; length = strlen( sptr ); if( length > 1 ) sptr++; break; default: *dptr++ = *sptr++; break; } } *dptr = '\0'; return( ( char *)&TempString[0] ); } return( (char *)NULL ); } /* SetStartItem() * ===================================================================== * This can be used to Set the start item on a submenu. This will * work of course, on the main menu too. */ void SetStartItem( int MenuID, int item ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); PopUpItem( PopPtr ) = item; } /* GetStartItem() * ===================================================================== * This can be used to get the start item in a submenu. Or at least * wot it currently thinks it is... */ int GetStartItem( int MenuID ) { POP_PTR PopPtr; PopPtr = GetMenuPtr( MenuID ); return( PopUpItem( PopPtr ) ); } /* END OF APPEARANCE OF ITEMS * ===================================================================== */ /* SUBMENU HANDLING * ===================================================================== */ /* DoSubMenu() * ===================================================================== * Display the submenu if necessary... * */ POP_PTR DoSubMenu( POP_PTR PopPtr, int obj ) { int num; POP_PTR SubPopPtr; GRECT SubRect; int MenuID; OBJECT *tree; int curx; ActiveTree( POBJECT( PopPtr ) ); SubPopPtr = ( POP_PTR )NULL; if( obj != -1 ) { num = FindNum( PopPtr, obj ); if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' ) { SubRect = ObRect( obj ); objc_offset( tree, obj, &SubRect.g_x, &SubRect.g_y ); MenuID = CmdSubMenu( PopPtr->CharPtr[ num ] ); SubPopPtr = GetMenuPtr( MenuID ); /* Displays faster if drawn on a byte boundary.*/ curx = SubRect.g_x + SubRect.g_w -1 - gl_wchar; curx = (( curx + 7 )/8)*8; if( !ShowSubMenu( MenuID, curx, SubRect.g_y, &SubRect )) SubPopPtr = ( POP_PTR )NULL; } } return( SubPopPtr ); } /* ShowSubMenu() * ===================================================================== * Display the Initial Submenu * RETURN: TRUE - AOK * FALSE - Memory allocation error. */ BOOLEAN ShowSubMenu( int MenuID, int xpos, int ypos, GRECT *rect ) { POP_PTR PopPtr; int max_height; PopPtr = GetMenuPtr( MenuID ); PPREV( PopPtr ) = ( POP_PTR )NULL; PXPOS( PopPtr ) = xpos; PYPOS( PopPtr ) = ypos; /* Take care of height of menu */ if( PHEIGHT( PopPtr ) > PNUM( PopPtr ) ) PHEIGHT( PopPtr ) = PNUM( PopPtr ); /* Limit Height to Height of screen - ( 2 * gl_hchars ) */ max_height = yres - ( 2 * gl_hchar ); while( ( PHEIGHT( PopPtr ) * gl_hchar ) > max_height ) PHEIGHT( PopPtr ) -= 1; PIX_HEIGHT( PopPtr ) = ( PHEIGHT( PopPtr ) * gl_hchar ); /* Take care of the width of the menu here...*/ PTEXTBUFFSIZE( PopPtr ) = CalcTextBufferSize( PopPtr ); PIX_WIDTH( PopPtr ) = ( PWIDTH( PopPtr ) * gl_wchar ); AdjustToScreen( PopPtr, xpos, ypos, rect, TRUE, FALSE ); if( Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 0 ) ) { if( Build_Objects( PopPtr )) return( TRUE ); } return( FALSE ); } /* HideSubMenu() * ===================================================================== * Hides the Submenu and free's any memory that it malloc'ed. */ void HideSubMenu( POP_PTR PopPtr ) { Pop_Blit( (long *)&PMEM( PopPtr ), &PObRect( PopPtr ), 1 ); if( POBJECT( PopPtr ) ) free( POBJECT( PopPtr ) ); if( PTEXTBUFF( PopPtr ) ) free( PTEXTBUFF( PopPtr ) ); POBJECT( PopPtr ) = ( OBJECT *)NULL; PTEXTBUFF( PopPtr ) = ( char *)NULL; } /* EvntSubMenu() * ==================================================================== * Submenu Handling... * IN: POP_PTR PopPtr * OUT: long -1L No Menu Items selected. * -2L We've rentered the submenu ( won't get to user ) * High Word Low Word * --------- -------- * Menu ID -1 Selected Disabled Item * Menu ID Menu Item Selected Menu Item. */ long EvntSubMenu( POP_PTR PopPtr ) { long result; /* Result to return with... */ MRETS mk; /* mouse structures */ int oldobj; /* old object */ int done = FALSE; /* Done flag to return.. */ int obj; /* object mouse is over... */ int event; /* Mouse button event flag */ /* TRUE = event occurred */ POP_PTR SubPopPtr; /* Pointer to Submenu... */ POP_PTR TempPtr; /* Temporary Submenu Pointer */ GRECT SubRect; /* ObRect of Submenu */ GRECT TempRect; /* Temp ObRect */ GRECT wall; /* ObRect for boundary checks */ GRECT BoxRect; /* ObRect for boundary checks */ GRECT DragRect; /* ObRect for Drag boundary */ OBJECT *tree; /* Object tree declaration */ int Action; /* Action that is occurring */ int num; /* temp number storage */ int ActiveObj; /* Current Active Object */ long CurTimeHz; /* Time in 200Hz...*/ BOOLEAN MenuDelayFlag; /* Display Submenu Delay in effect*/ BOOLEAN MenuDragFlag; /* Drag Submenu Delay in effect */ BOOLEAN DragStart; /* Start Drag Submenu Delay... */ int MenuObject; /* Object to start the timer on*/ int OldY; /* Old Y pos for a drag */ int OldX; ActiveTree( POBJECT( PopPtr ) ); Graf_mkstate( &mk ); oldobj = -1; obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y ); SubPopPtr = ( POP_PTR )NULL; BoxRect = ObRect( ROOT ); result = -1L; OldY = 0; OldX = 0; MenuDelayFlag = FALSE; MenuDragFlag = FALSE; DragStart = FALSE; ActiveObj = -1; BSTATE = BREAL = 0; do { if( obj != -1 ) { if( obj != oldobj ) { wall = ObRect( obj ); objc_offset( tree, obj, &wall.g_x, &wall.g_y ); if( SubPopPtr ) { PPREV( SubPopPtr ) = ( POP_PTR )NULL; HideSubMenu( SubPopPtr ); SubPopPtr = ( POP_PTR )NULL; MenuDragFlag = FALSE; MenuDelayFlag = FALSE; DragStart = FALSE; ActiveObj = -1; } if(( oldobj != -1 ) && ( oldobj != obj )) deselect( tree, oldobj ); if( ( obj == PFIRST( PopPtr ) && strchr( ObString( obj ), UP_ARROW ) ) || ( obj == PLAST( PopPtr ) && strchr( ObString( obj ), DOWN_ARROW ) ) ) deselect( tree, obj ); else { if( !IsDisabled( obj ) ) { select( tree, obj ); if( ( num = FindNum( PopPtr, obj )) != -1 ) { if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' ) { Supexec( GetTimeHz ); CurTimeHz = TimeInHz; MenuDelayFlag = TRUE; MenuDragFlag = FALSE; DragStart = FALSE; MenuObject = obj; } } } } } } else { if(( oldobj != -1 ) && ( oldobj != obj ) ) { deselect( tree, oldobj ); } } oldobj = obj; Graf_mkstate( &mk ); OldY = mk.y; OldX = mk.x; do { BSTATE = 0; if( MenuDragFlag ) { if( ( !xy_inrect( mk.x, mk.y, &wall ) || ( SubPopPtr && xy_inrect( mk.x, mk.y, &SubRect ) )) ) { if( DragStart ) { Supexec( GetTimeHz ); CurTimeHz = TimeInHz; DragStart = FALSE; DragRect.g_x = mk.x; DragRect.g_y = SubRect.g_y; DragRect.g_h = SubRect.g_h; OldX = mk.x; OldY = mk.y; if( mk.x <= SubRect.g_x ) { DragRect.g_w = SubRect.g_w + ( SubRect.g_x - DragRect.g_x ); } else { DragRect.g_x = SubRect.g_x; DragRect.g_w = mk.x - SubRect.g_x + 1; } } if( SubPopPtr ) { if( !DragStart ) { Supexec( GetTimeHz ); if( ( TimeInHz - CurTimeHz ) >= SUBDRAG_DELAY ) MenuDragFlag = FALSE; if( !xy_inrect( mk.x, mk.y, &DragRect ) || (xy_inrect( mk.x, mk.y, &DragRect ) && ( mk.x == OldX ) && ( mk.y != OldY ) ) ) MenuDragFlag = FALSE; else { DragRect.g_x = mk.x; if( mk.x <= SubRect.g_x ) { DragRect.g_w = SubRect.g_w + ( SubRect.g_x - DragRect.g_x ); } else { DragRect.g_x = SubRect.g_x; DragRect.g_w = mk.x - SubRect.g_x + 1; } } } /* We're in the new submenu...*/ if( xy_inrect( mk.x, mk.y, &SubRect ) ) { Action = 0; MenuDragFlag = FALSE; break; } MenuDelayFlag = FALSE; } } } else { BoxRect = ObRect( ROOT ); if( xy_inrect( mk.x, mk.y, &BoxRect ) ) { /* We're in our current submenu still * but we might be in a new menu item. */ Action = 1; break; } else { /* Checking to see if we're in any of the previous menus */ TempPtr = PPREV( PopPtr ); while( TempPtr ) { /* We're in a previous submenu or root */ TempRect = PObRect( TempPtr ); if( xy_inrect( mk.x, mk.y, &TempRect ) ) { Action = 2; goto abort; } TempPtr = PPREV( TempPtr ); } } /* if we reach here, we're outside of all the rectangles.*/ if( SubPopPtr ) { HideSubMenu( SubPopPtr ); PPREV( SubPopPtr ) = ( POP_PTR )NULL; SubPopPtr = ( POP_PTR )NULL; ActiveObj = -1; MenuDragFlag = FALSE; MenuDelayFlag = FALSE; } if( oldobj != -1 ) { deselect( tree, oldobj ); oldobj = obj = -1; } } Action = -1; OldX = mk.x; OldY = mk.y; Evnt_timer( 0L ); /* Kludge to put in a delay * so that we can get a new mouse * x,y with > 1 pixel difference */ Graf_mkstate( &mk ); }while( !BSTATE ); abort: /* we've exited due to a button click, OR * we've entered one of the rectangles. * Let's see wot we've got. * If Action == -1, then the user clicked outside of * ANY rectangles and we should just exit and close * down all of the menus. */ Graf_mkstate( &mk ); obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y ); /* Fix for when the submenu is displayed and we click * on its title to exit. */ if( xy_inrect( mk.x, mk.y, &wall ) && SubPopPtr && MenuDragFlag && ( BSTATE == 1 ) ) Action = 1; event = FALSE; switch( Action ) { case 0: /* Entered the New Submenu, if there is one */ if( SubPopPtr ) { /* need to check wot is returned...*/ result = EvntSubMenu( SubPopPtr ); if(( result == -1L ) || ( result != -2L ) ) { obj = -1; event = TRUE; goto leave; } TempPtr = SubPopPtr; PPREV( SubPopPtr ) = ( POP_PTR )NULL; HideSubMenu( SubPopPtr ); SubPopPtr = ( POP_PTR )NULL; MenuDragFlag = FALSE; MenuDelayFlag = FALSE; Graf_mkstate( &mk ); obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y ); num = FindNum( PopPtr, obj ); if( ( obj != -1 ) && ( ActiveObj == obj ) && ( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' ) && ( GetMenuPtr( CmdSubMenu( PopPtr->CharPtr[ num ] )) == TempPtr ) ) { if( !IsSelected( obj ) ) select( tree, obj ); if( ( SubPopPtr = DoSubMenu( PopPtr, obj )) != ( POP_PTR )NULL ) { ActiveObj = obj; SubRect = PObRect( SubPopPtr ); ActiveTree( POBJECT( PopPtr ) ); PPREV( SubPopPtr ) = PopPtr; MenuDragFlag = TRUE; DragStart = TRUE; wall = ObRect( obj ); objc_offset( tree, obj, &wall.g_x, &wall.g_y ); } } else ActiveObj = -1; } break; case 1: /* we are still in the current submenu...*/ /* The user click on an arrow button? */ if( (( BSTATE == 1 ) || ( BREAL == 1 )) && (( obj == PFIRST( PopPtr) && strchr( ObString( obj ), UP_ARROW )) || (( obj == PLAST( PopPtr ) ) && strchr( ObString( obj ), DOWN_ARROW )) )) { if( Pop_Arrow( PopPtr, obj ) ) { if( ( num = FindNum( PopPtr, obj )) != -1 ) { if( CmdFlag( PopPtr->CharPtr[ num ] ) == '~' ) { Supexec( GetTimeHz ); CurTimeHz = TimeInHz; MenuDelayFlag = TRUE; MenuObject = obj; } } } } else { if( !SubPopPtr && MenuDelayFlag && ( MenuObject == obj )) { Supexec( GetTimeHz ); if( ( TimeInHz - CurTimeHz ) >= SUBMENU_DELAY ) { MenuDelayFlag = FALSE; if( !IsSelected( obj ) ) select( tree, obj ); if( ( SubPopPtr = DoSubMenu( PopPtr, obj )) != ( POP_PTR )NULL ) { ActiveObj = obj; SubRect = PObRect( SubPopPtr ); ActiveTree( POBJECT( PopPtr ) ); PPREV( SubPopPtr ) = PopPtr; MenuDragFlag = TRUE; DragStart = TRUE; wall = ObRect( obj ); objc_offset( tree, obj, &wall.g_x, &wall.g_y ); } } } else MenuDelayFlag = FALSE; /* we are just in another menu item. * If a submenu was displayed, hide it. */ if( SubPopPtr && ( ActiveObj != obj ) ) { PPREV( SubPopPtr ) = ( POP_PTR )NULL; HideSubMenu( SubPopPtr ); SubPopPtr = ( POP_PTR )NULL; MenuDragFlag = FALSE; MenuDelayFlag = FALSE; ActiveObj = -1; } /* if the buttons are down, they clicked on something*/ if(( BSTATE == 1 ) || ( BREAL == 1 )) { event = TRUE; obj = objc_find( tree, ROOT, MAX_DEPTH, mk.x, mk.y ); CurMenu = PMENUID( PopPtr ); CurObject = obj; result = 0L; } } break; case 2: /* we have entered a previous submenu... */ result = -2L; case -1: /* we have clicked outside of ANY submenus.*/ if( Action == -1 ) { /* don't exit if not a left button click */ if( BSTATE != 1 ) goto leave; } CurMenu = CurObject = -1; default: event = TRUE; obj = -1; break; } leave: if( event ) { if( SubPopPtr ) { PPREV( SubPopPtr ) = ( POP_PTR )NULL; HideSubMenu( SubPopPtr ); SubPopPtr = ( POP_PTR )NULL; } done = TRUE; } }while( !done ); if(( obj != -1 ) && ( result != -2L ) && ( result != -1L ) ) { result = 0L; result = ( int )PMENUID( PopPtr ); result = ( result << 16L ); if( !IsDisabled( obj ) ) obj = FindNum( PopPtr, obj ); else obj = -1; result |= ( obj & 0x0000FFFF ); } return( result ); } /* FindNum() * ===================================================================== * Given the object #, find the adjusted index of the menu item. */ int FindNum( POP_PTR PopPtr, int obj ) { int offset; offset = POFFSET( PopPtr ); if( offset > 0 ) offset -= 1; obj -= PFIRST( PopPtr ); obj += offset; return( obj ); } /* MenuChoice() * ===================================================================== * Returns the CURRENT values of the menu and object clicked on. * Call ONLY after the PopMenuSelect() call has returned. */ long MenuChoice( void ) { long value; POP_PTR PopPtr; int obj; value = 0L; value = ( int )CurMenu; value = ( value << 16L ); if( CurMenu != -1 ) { PopPtr = GetMenuPtr( CurMenu ); obj = FindNum( PopPtr, CurObject ); } else obj = -1; value |= ( obj & 0x0000FFFF ); return( value ); } /* END OF SUBMENU ROUTINES * ===================================================================== */ /* SUBMENU DELAY ROUTINES * ===================================================================== */ /* GetTimeHz() * ===================================================================== * Gets the System time in 20ms ticks. */ long GetTimeHz( void ) { long *ptr; ptr = ( long *)0x4BA; TimeInHz = *ptr; return( TimeInHz ); } /* SetSubMenuDelay() * ===================================================================== * Sets the length of time before a submenu appears as the user drags * the pointer thru a menu list. * The Units are in # of Milliseconds. ( 1000 = 1 second ). */ void SetSubMenuDelay( long ms ) { if( ms < 0L ) ms = INIT_DISPLAY_DELAY; if( ms ) ms /= 20L; SUBMENU_DELAY = ms; } /* SetSubDragDelay() * ===================================================================== * Sets the length of time the user has to drag diagonally, a pointer * to an active submenu before the submenu closes. */ void SetSubDragDelay( long ms ) { if( ms < 0L ) ms = INIT_DRAG_DELAY; if( ms ) ms /= 20L; SUBDRAG_DELAY = ms; } /* END OF SUBMENU DELAY ROUTINES * ===================================================================== */ /* AdjustToScreen() * ===================================================================== * Takes the Submenu XY and adjusts it so that it fits within * the screen area still. If there is an optional object, we will * adjust around that. * If we DO adjust, we'll try to get it on a byte boundary. * BOOLEAN byte_flag - a flag to align the xpos on a byte boundary. * - TRUE - YES! * - NOTE: if the menu does not want byte adjusting * BUT it is off the screen, we will adjust * it back ONTO the screen on a byte boundary. * We just won't byte_adjust it if its already * on the screen in its entirety. * BOOLEAN Display_Flag; - a flag for vertical alignment of submenus. * - When the submenu extends BELOW the bottom * of the screen, there are 2 ways to adjust * the menu. * 1)Adjust by displaying the menu upwards vertically * from the rectangle button. * 2)Adjust by moving the menu upwards 1 gl_hchar * at a time until the menu does not extend * below the screen boundary. * TRUE - Use Version 1 * FALSE - Use Version 2 */ void AdjustToScreen( POP_PTR PopPtr, int xpos, int ypos, GRECT *rect, BOOLEAN byte_flag, BOOLEAN Display_Flag ) { int temp; int position; /* Handle the X Coordinate Adjustments... *------------------------------------------------------------------ */ temp = xpos + PIX_WIDTH( PopPtr ); if( temp > ( xres - 4 )) { if( !byte_flag ) { temp -= ( xres - 4 ); temp = ( temp + gl_wchar )/gl_wchar; temp *= gl_wchar; xpos -= temp; } else { xpos = rect->g_x - PIX_WIDTH( PopPtr ); xpos = ( xpos + gl_wchar )/gl_wchar; xpos *= gl_wchar; } } else { if( byte_flag ) { xpos = ( xpos + ( gl_wchar - 1 ) )/gl_wchar; xpos *= gl_wchar; } } /* Handle X limits < 0 */ while( xpos <= 0 ) xpos += gl_wchar; PXPOS( PopPtr ) = xpos; /* Handle the Y Coordinate Adjustments... *------------------------------------------------------------------ */ /* Calculates the Position so that the submenu start item * is adjacent to the button that activated it. */ position = ( PopUpItem( PopPtr ) + ( PNUM( PopPtr ) > PHEIGHT( PopPtr ) )) / PHEIGHT( PopPtr ); if( position ) { position = PopUpItem( PopPtr ); temp = PNUM( PopPtr ) - PHEIGHT( PopPtr ) + 1; if( position >= temp ) position = temp; position -= 1; } position = PopUpItem( PopPtr ) - position; ypos = ypos - ( position * gl_hchar ); /* Calculates a new position if the submenu exceeds the * height of the screen. */ temp = ypos + PIX_HEIGHT( PopPtr ); if( temp > ( yres - 4 )) { if( Display_Flag ) ypos = ypos - ( (PHEIGHT( PopPtr ) - position - 1 )* gl_hchar ); else { do { ypos -= gl_hchar; temp = ypos + PIX_HEIGHT( PopPtr ); }while( temp > ( yres - 4 ) ); } } while( ypos <= ( gl_hchar + ( gl_hchar/2 ) )) ypos += gl_hchar; PYPOS( PopPtr ) = ypos; } /* WaitForUpButton() * ===================================================================== * Waits for an up button. */ void WaitForUpButton( void ) { MRETS mk; do { Graf_mkstate( &mk ); }while( mk.buttons ); }