Function AChoice() Foundation

Displays and allows selection from a list box.

AChoice( <nTop>         , ;
         <nLeft>        , ;
         <nBottom>      , ;
         <nRight>       , ;
         <aItems>       , ;
        [<alSelectable>], ;
        [<bcUserFunc>]  , ;
        [<nStartItem>]  , ;
        [<nStartRow> ]  ) --> nElement
<nTop> is a numeric integer value specifying the top screen row for the AChoice() list box window. The four arguments <nTop>, <nLeft>, <nBottom> and <nRight> determine the screen coordinates for the window. Row positions for the window can range from zero to MaxRow() and column positions can range from zero to MaxCol().
<nLeft> is an integer numeric value specifying the left screen column for the window. The default value is zero.
<nBottom> is an integer numeric value specifying the bottom screen row for the window. The default value is MaxRow().
<nRight> is an integer numeric value specifying the right screen column for the window. The default value is MaxCol().
<aItems> is an array whose elements must be of data type 'character'. These values are displayed in the list box and identified by their numeric position in the array.
<alSelectable> contains either a logical value or an array which parallels <aItems> and specifies whether individual items in <aItems> are selectable. When <alSelectable> has the value .F. (false), the values in <aItems> are only displayed and cannot be selected with the arrow keys. If <alSelectable> is .T. (true), all elements in <aItems> are available for selection. To specify whether individual elements in <aItems> can be selected, <alSelectable> must be an array of the same length as <aItems>. Each element in the array should be a logical value indicating whether the corresponding element in <aItems> is selectable. The logical value .T. (true) indicates that the corresponding element is selectable and the value .F. (false) indicates it is not selectable. All elements are displayed but non-selectable elements cannot be selected with the arrow keys (the highlight jumps such elements). Instead of logical values, <alSelectable> can contain expressions in the form of character strings which provide a logical value when they are macro expanded. <alSelectable> can also contain code blocks which return logical values when executed. When <alSelectable> is not included, all elements in <aItems> are selectable.
<bcUserFunc> is either a code block or a character string containing the name of a user-defined function. When <bcUserFunc>contains a code block, it is executed after each keypress not processed by AChoice(). Similarly, when the function name of a user function is passed as a character string (without parentheses), this function is called after each keypress not processed by AChoice(). If a function name is specified, the function may not be declared STATIC, but if a code block is specified, it can contain a call to a STATIC FUNCTION.
<nStartItem> is an integer numeric value indicating the position in <aItems> of the element selected and highlighted when AChoice() is called. When this element is not selectable or when <nStartItem> is not specified, the highlight is positioned at the first element of <aItems>.
<nStartRow> is an integer numeric value which determines the row within the achoice window where the highlight is positioned when AChoice() is called. The range for <nStartRow> is zero to <nBottom> - <nTop> or Len(<aItems>). The default value is zero.

If the user exits AChoice() by pressing the return key, the return value of AChoice() is an integer corresponding to the position of the selected element in <aItems>. If the user terminates AChoice() by pressing the Esc key, the return value is zero.


The function AChoice() allows the selection of a value from a one dimensional array. It is suitable for generating menus, programming combo boxes or displaying lists. The elements contained in the array <aItems> are displayed in a window. When the array has more elements than the number of rows in the window, the window contents are scrolled using the arrow keys to allow the user to select an element not currently visible within the window. If <aItems> contains character strings, pressing the first letter of the character string causes the highlight to jump directly to the next element beginning with that character.

Elements in <aItems> are displayed in the system colors set with SetColor(). All selectable elements are displayed in the default color (first color value), the current element is displayed in the "highlighted" color (second color value) and unselectable elements are displayed in the "not selected" color (fifth color value).

Cursor navigation in AChoice() depends on the user function or code block specified. When <bcUserFunc> is not included, navigation occurs using the following keys:

AChoice() Navigation keys with no user function specified
Key Inkey constants Action
Up Arrow K_UP Previous element
Down Arrow K_DOWN Next element
Left Arrow K_LEFT Terminates AChoice()
Right Arrow K_RIGHT Terminates AChoice()
Home K_HOME First element
End K_END Last element
Page Up K_PGUP Next screen up
Page Down K_PGDN Next screen down
Ctrl+Home K_CTRL_HOME Beginning of the window
Ctrl+End K_CTRL_END End of the window
Ctrl+Page Up K_CTRL_PGUP First element
Ctrl+Page Down K_CTRL_PGDN Last element
Return K_RETURN Selects element
Esc K_ESC Terminates AChoice()
Letter Cursor to next element beginning with this letter

If the function SetMouse(.T.) is called before calling Achoice(), a selection can also be made using the mouse. Mouse navigation in Achoice() is as follows:

Mouse navigation with Achoice()
Action Description
Single left click Highlight is moved to the mouse pointer
Double left click Selection is made, Achoice() exits
Press left mouse button and drag mouse Highlight follows the mouse pointer if pointer moves out of the Achoice(), window, Achoice() scrolls the list

If Achoice() is used with Xbase Parts in the hybrid mode, events for an Xbase Part are automatically sent to the applicable Xbase Part and are not processed by Achoice().

Achoice() with a user function

The optional argument <bcUserFunc> modifies the functionality of AChoice(). <bcUserFunc> contains either a character string with the name of a function or a code block.

If <bcUserFunc> contains a function name, the corresponding function must not be declared STATIC and must be specified as a character string without parentheses ().

When <bcUserFunc> contains a code block, it must contain the function call to the user function. If a code block is used, the user function within it can be declared STATIC.

The user function allows user specific configuration of individual keys for AChoice(). When a user function is included, AChoice() itself only processes the following keys:

AChoice() Navigation keys when a user function is specified
Key Inkey constant Action
Up Arrow K_UP Previous element
Down Arrow K_DOWN Next element
Page Up K_PGUP Next screen up
Page Down K_PGDN Next screen down
Ctrl+Home K_CTRL_HOME Beginning of window
Ctrl+End K_CTRL_END End of window
Ctrl+Page Up K_CTRL_PGUP First element
Ctrl+Page Down K_CTRL_PGDN Last element

If a value is specified for <bcUserFunc> in the call to the function AChoice(), AChoice() calls the user function (or evaluates the code block) when any key is pressed which AChoice() does not automatically process. Configurable keys are processed in the user function and the RETURN value of the user function notifies AChoice() how it should react to the keypress.

When AChoice() calls the user function, it passes three numeric values to the user function: the current AChoice() mode, the current element in <aItems> and the current row within the window. The value of the current row is in relation to <nTop> and begins with zero.

The AChoice() mode designates the present condition of AChoice() and depends on the last key pressed or the last action executed by AChoice() prior to the call of the user function. In the header file symbolic constants are defined which identify the different modes of AChoice():

Modes of AChoice() (see
Constants Description
AC_IDLE Idle, all keys processed
AC_HITTOP Attempt to move cursor to an element < 1
AC_HITBOTTOM Attempt to move cursor to an element > Len(<aItems>)
AC_EXCEPT Key or event is unknown
AC_NOITEM No selectable elements available

The mode AC_IDLE means that no more keys are available for processing and AChoice() is waiting for a new keypress. Each time AChoice() is unable to find another value in the keyboard buffer, the user function is called once and then AChoice() waits until a new key is pressed.

The modes AC_HITTOP and AC_HITBOTTOM signal that the user has tried to position the highlight before the first element or past the last element in <aItems>.

The mode AC_EXCEPT indicates that an unknown or configurable key code needs processing or that a mouse event occurred. The action AChoice() should execute after a configurable key press can be defined by the return value of the user function. The return value AC_CONT causes AChoice() to execute its default action. If the value returned is not equal to AC_CONT, AChoice() executes some other action. Symbolic constants are defined in the header file for the possible return values of the user function and identify the various AChoice() actions that can be requested by the user function:

Return values of the AChoice() user function (see
Constants Action
AC_ABORT Terminates AChoice() without selection
AC_SELECT Exits AChoice() with selection
AC_CONT Continues AChoice()
AC_GOTO Jumps to next element whose first letter matches the key

Selecting a PRG file

// In the example, a simple call to AChoice() is shown. 
// This selects a file name from an array created 
// with Directory(). Only PRG files are listed. When a 
// selection is made, the file is edited with MemoEdit(). 

   LOCAL aFiles := Directory("*.PRG"), nChoice 

                                    // create a single 
                                    // dimensional array 
   AEval( aFiles, {|a,i| a:=Upper(a[1])},,,.T. ) 

   nChoice := AChoice( 2, 2, 20, 13, aFiles ) 

   IF nChoice > 0 
      MemoEdit( MemoRead( aFiles[ nChoice ] ) ) 


AChoice() with a user function
// The example shows the function SelectFile() used for selecting 
// files. The file names are retrieved using Directory() and 
// displayed with AChoice(). In the user function StatusBar(), 
// a status bar is displayed in the right window border 
// which identifies the position of the current element 
// in the array. Note the STATIC variables visible file-wide. 

#include "" 
#include "" 

STATIC saFiles, snTop, snLeft, snBottom, snRight 

FUNCTION SelectFile() 
   LOCAL nChoice, aGray, cScreen 

   saFiles := Directory("*.*")      // create array 
   snTop   := 2                     // place coordinates in 
   snLeft  := 2                     // STATIC variables 
   snBottom:= 20 
   snRight := 13 

   AEval( saFiles, {|a,i| a:=Upper(a[1])},,, .T. ) 
   aGray := Array( Len(saFiles) ) 
                                    // EXE files are not 
                                    // selectable 
   AEval( saFiles, {|c,i| aGray[i]:= (! ".EXE" $ c) } ) 

                                    // save screen 
   cScreen := SaveScreen(snTop-1, snLeft-1, snBottom+1, snRight+1) 
                                    // output border 
   DispBox( snTop-1, snLeft-1, snBottom+1, snRight+1 ) 
                                    // allow file to be selected 
   nChoice := AChoice( snTop  , snLeft, snBottom, snRight, ; 
                       saFiles, aGray , "STATUSBAR" ) 

                                    // restore screen 
   RestScreen(snTop-1, snLeft-1, snBottom+1, snRight+1, cScreen) 

RETURN IIf( nChoice == 0, "", saFiles[ nChoice ] ) 

FUNCTION StatusBar( nMode, nElement, nRow ) 
   LOCAL nReturn := AC_CONT        // default: continues 
   LOCAL nKey    := LastKey() 
   LOCAL nLen    := Len( saFiles ) 
   LOCAL nPos 

   CASE nMode == AC_IDLE           // output status bar 
      IF nElement == 1             // determine position for 
         nPos := 1                 // indicator 
      ELSEIF nElement == nLen 
         nPos := snBottom-snTop-1 
         nPos := Round( (snBottom-snTop-1) * (nElement/nLen), 0 ) 
      nPos := Max( 1, nPos ) 

      DispBox( snTop, snRight+1, snBottom, snRight+1, Chr(176) ) 
      @ snTop     , snRight+1 SAY Chr(24)  // arrow up 
      @ snBottom  , snRight+1 SAY Chr(25)  // arrow down 
      @ snTop+nPos, snRight+1 SAY Chr(219) // indicator 

   CASE nMode == AC_HITTOP         // beginning is reached 
      Tone( 1000, 2 ) 

   CASE nMode == AC_HITBOTTOM      // end is reached 
      Tone( 500,  2 ) 

   CASE nMode == AC_EXCEPT 
      DO CASE 
      CASE nKey == K_RETURN 
         nReturn := AC_SELECT      // select element 

      CASE nKey == K_ESC 
         nReturn := AC_ABORT       // terminate AChoice() 

         nReturn := AC_GOTO        // jump to element 


RETURN nReturn 
Menu using AChoice() for a simple text editor

// The example shows a simple text editor with a menu. 
// The menu is called by the F10 key, and it permits 
// reading and saving text files. Text is edited 
// using MemoEdit(). The menu is created with AChoice(). 
// Selection of files occurs using the function SelectFile() 
// from the previous example "AChoice() with user function". 

#include "" 

STATIC saMenu, saGray, saMsg, saAction 
STATIC scFileName := "TEST.TXT" 
STATIC scText     := "" 

   SetColor( "N/BG,W+/B,,,N+/BG" ) 

   saMenu := { "New file      ", ; // menu items 
               "Open file     ", ; 
               "Save          ", ; 
               "Save As       ", ; 
               "--------------", ; // line separator 
               "Quit"            } 

                                        // line separator is 
   saGray := {.T.,.T.,.T.,.T.,.F.,.T.}  // not selectable 

                                    // menu "Messages" 
   saMsg  := { "Create new text file"          , ; 
               "Opens and edit text file"      , ; 
               "Save text to current file"     , ; 
               "Saves text to another file"    , ; 
               ""                              , ; 
               "Quit program"                    } 

                                    // menu actions in code blocks 
   saAction := { {|| NewFile()    }, ; 
                 {|| OpenFile()   }, ; 
                 {|| SaveFile()   }, ; 
                 {|| SaveFileAs() }, ; 
                                   , ; 
                 {|| Quit()       }  } 

   SET KEY K_F10 TO MemoQuit()      // F10 terminates MemoEdit() 
   scText := MemoRead( scFileName ) 

   DO WHILE .T. 
      @ 0,0 SAY " Texteditor: F10 calls menu" 
      scText := MemoEdit( scText )  // edit text 
      TextMenu()                    // call menu 


PROCEDURE TextMenu()                // display menu with AChoice() 
   LOCAL cScreen := SaveScreen(), nChoice 
   DispBox( 1, 1, 8, 17 ) 
   nChoice := AChoice( 2, 2, 7, 16, saMenu, saGray ) 
   RestScreen( ,,,, cScreen ) 

   IF nChoice > 0                  // selection is made which 
      Eval( saAction [nChoice] )   // causes action 

PROCEDURE MemoQuit()               // terminate MemoEdit() 

PROCEDURE Quit()                   // terminate the program 

PROCEDURE NewFile()                // create new file 
   LOCAL cFilename := GetFileName() 
   IF ! Empty( cFileName ) 
      MemoWrit( scFileName, scText ) 
      scFileName := cFileName 
      scText     := "" 

PROCEDURE OpenFile()               // open and read file 
   LOCAL cFilename := SelectFile() // function from previous example 
   IF ! Empty( cFileName ) 
      MemoWrit( scFileName, scText ) 
      scFileName := cFileName 
      scText     := MemoRead( scFileName ) 

PROCEDURE SaveFile()               // save file 
   MemoWrit( scFileName, scText ) 

PROCEDURE SaveFileAs()             // save file with new name 
   LOCAL cFileName := GetFileName() 
   IF ! Empty( cFileName ) 
      scFileName := cFilename 
      MemoWrit( scFileName, scText ) 

FUNCTION GetFileName()             // request file name 
   LOCAL cScreen := SaveScreen(), cFileName := Space(12) 
   @ 0,0 SAY "Enter file name:" GET cFileName 
RETURN cFileName 


If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.